2024年10月单例模式双重校验锁(如何高效的创建一个线程安全的单例)

 更新时间:2024-10-12

  ⑴单例模式双重校验锁(如何高效的创建一个线程安全的单例

  ⑵如何高效的创建一个线程安全的单例

  ⑶单例模式的概念单例模式就是确保只有一个实例,而且自行实例化并向整个系统传递这个实例,这个类就称作为单例类单例模式最重要的一个特点就是构造方法私有化。创建单例模式分为懒汉式和饿汉式。第一种:懒汉式(线程不安全的传统的懒汉式创建单例模式,是线程不安全的[java]viewplaincopyprint?publilassSingleton{privatestaticSingletoninstance;privateSingleton(){}publicstaticSingletongetInstance(){if(instance==null){instance=newSingleton();}returninstance;}}所谓懒汉式单例模式就是在调用的时候才去创建这个实例。这种写法的懒加载很明显,但是缺点就是不能再多线程访问下正常工作。第二种:懒汉式(线程安全的[java]viewplaincopyprint?publilassSingleton{privatestaticSingletoninstance;privateSingleton(){}publicstaticsynchronizedSingletongetInstance(){if(instance==null){instance=newSingleton();}returninstance;}}线程安全的方式创建单例就是在对外的创建实例方法上加上synchronized。这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazyloading,但是,遗憾的是,效率很低,%情况下不需要同步。第三种:饿汉式(是线程安全的[java]viewplaincopyprint?publilassSingleton{privatestaticSingletoninstance=newSingleton();privateSingleton(){}publicstaticSingletongetInstance(){returninstance;}}这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法,但是也不能确定有其他的方式(或者其他的静态方法导致类装载,这时候初始化instance显然没有达到lazyloading的效果。第四种:静态内部类的方式创建单例模式(staticinnerclass[java]viewplaincopyprint?publilassSingleton{privateSingleton(){}privatestatilassSingletonHolder{//静态内部类privatestaticSingletonsingleton=newSingleton();}publicstaticSingletongetInstance(){returnSingletonHolder.singleton;}}这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种方式不同的是(很细微的差别:第三种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazyloading效果,而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化singleton。想象一下,如果实例化singleton很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三种方式就显得很合理。第五种:双重校验锁(dubblecheckinstance[java]viewplaincopyprint?publilassSingleton{privatestaticSingletonsingleton;privateSingleton(){}publicstaticSingletongetInstance(){if(singleton==null){synchronized(Singleton.class){if(singleton==null){/***为什么这里会使用双重判定呢?*/singleton=newSingleton();}}}returnsingleton;}}这种事用双重判断来创建一个单例的方法,那么我们为什么要使用两个if判断这个对象当前是不是空的呢?因为当有多个线程同时要创建对象的时候,多个线程有可能都停止在第一个if判断的地方,等待锁的释放,然后多个线程就都创建了对象,这样就不是单例模式了,所以我们要用两个if来进行这个对象是否存在的判断。

  ⑷单例模式的双重加锁机制为什么要两次检查,第一次检查完不是已经知道了吗

  ⑸第一张图:针对的是多线程场景如果两个线程,同时进行为空判断,都为true。此时如果没有lock+第二个判断,必然会创建两个实例。第二张图:多线程场景可以搞定,但是性能太差所有针对单例的访问,都需要被锁住,然后一个一个执行。性能太差了。

  ⑹什么是双重检查锁定,为什么要进行双重检查锁定

  ⑺单例类在Java开发者中非常常用,但是它给初级开发者们造成了很多挑战。他们所面对的其中一个关键挑战是,怎样确保单例类的行为是单例?也就是说,无论任何原因,如何防止单例类有多个实例。在整个应用生命周期中,要保证只有一个单例类的实例被创建,双重检查锁(DoublecheckedlockingofSingleton是一种实现方法。顾名思义,在双重检查锁中,代码会检查两次单例类是否有已存在的实例,一次加锁一次不加锁,一次确保不会有多个实例被创建。顺便提一下,在JDK.中,Java修复了其内存模型的问题。在JDK.之前,这种方法会有问题。本文中,我们将会看到怎样用Java实现双重检查锁的单例类,为什么Java之前的版本双重检查锁会有问题,以及怎么解决这个问题。顺便说一下,这也是重要的面试要点,我曾经在金融业和服务业的公司面试被要求手写双重检查锁实现单例模式、相信我,这很棘手,除非你清楚理解了你在做什么。你也可以阅读我的完整列表“单例模式设计问题”来更好的准备面试。为什么你需要双重检查锁来实现单例类?一个常见情景,单例类在多线程环境中违反契约。如果你要一个新手写出单例模式,可能会得到下面的代码:privatestaticSingleton_instance;publicstaticSingletongetInstance(){if(_instance==null){_instance=newSingleton();}return_instance;}然后,当你指出这段代码在超过一个线程并行被调用的时候会创建多个实例的问题时,他很可能会把整个getInstance()方法设为同步(synchronized,就像我们展示的第二段示例代码getInstanceTS()方法一样。尽管这样做到了线程安全,并且解决了多实例问题,但并不高效。在任何调用这个方法的时候,你都需要承受同步带来的性能开销,然而同步只在第一次调用的时候才被需要,也就是单例类实例创建的时候。这将促使我们使用双重检查锁模式(doublecheckedlockingpattern,一种只在临界区代码加锁的方法。程序员称其为双重检查锁,因为会有两次检查_instance==null,一次不加锁,另一次在同步块上加锁。这就是使用Java双重检查锁的示例:publicstaticSingletongetInstanceDC(){if(_instance==null){//SingleCheckedsynchronized(Singleton.class){if(_instance==null){//Doublechecked_instance=newSingleton();}}}return_instance;}这个方法表面上看起来很完美,你只需要付出一次同步块的开销,但它依然有问题。除非你声明_instance变量时使用了volatile关键字。没有volatile修饰符,可能出现Java中的另一个线程看到个初始化了一半的_instance的情况,但使用了volatile变量后,就能保证先行发生关系(happens-beforerelationship。对于volatile变量_instance,所有的写(write都将先行发生于读(read,在Java之前不是这样,所以在这之前使用双重检查锁有问题。现在,有了先行发生的保障(happens-beforeguarantee,你可以安全地假设其会工作良好。另外,这不是创建线程安全的单例模式的最好方法,你可以使用枚举实现单例模式,这种方法在实例创建时提供了内置的线程安全。另一种方法是使用静态持有者模式(staticholderpattern。/**AjourneytowritedoublecheckedlockingofSingletonclassinJava.*/classSingleton{privatevolatilestaticSingleton_instance;privateSingleton(){//preventingSingletonobjectinstantiationfromoutside}/**stversion:createsmultipleinstanceiftwothreadaess*thismethodsimultaneously*/publicstaticSingletongetInstance(){if(_instance==null){_instance=newSingleton();}return_instance;}/**ndversion:thisdefinitelythread-safeandonly*createsoneinstanceofSingletononconcurrentenvironment*butunnecessarilyexpensiveduetocostofsynchronization*ateverycall.*/publicstaticsynchronizedSingletongetInstanceTS(){if(_instance==null){_instance=newSingleton();}return_instance;}/**rdversion:AnimplementationofdoublecheckedlockingofSingleton.*Intentionistominimizecostofsynchronizationandimproveperformance,*byonlylockingcriticalsectionofcode,thecodewhichcreatesinstanceofSingletonclass.*Bythewaythisisstillbroken,ifwedon’tmake_instancevolatile,asanotherthreadcan*seeahalfinitializedinstanceofSingleton.*/publicstaticSingletongetInstanceDC(){if(_instance==null){synchronized(Singleton.class){if(_instance==null){_instance=newSingleton();}}}return_instance;}}这就是本文的所有内容了。这是个用Java创建线程安全单例模式的有争议的方法,使用枚举实现单例类更简单有效。我并不建议你像这样实现单例模式,因为用Java有许多更好的方式。但是,这个问题有历史意义,也教授了并发是如何引入一些微妙错误的。正如之前所说,这是面试中非常重要的一点。在去参加任何Java面试之前,要练习手写双重检查锁实现单例类。这将增强你发现Java程序员们所犯编码错误的洞察力。另外,在现在的测试驱动开发中,单例模式由于难以被模拟其行为而被视为反模式(antipattern,所以如果你是测试驱动开发的开发者,最好避免使用单例模式。

  ⑻Java单例模式双重检验锁的第一次是否为空判断是什么目的

  ⑼第一次判断是否为空是位了保证是单例,只有初始是空的情况下才可以创建,synchronize里头的if判空是为了避免小概率事件发生,比如当对象还没创建时,有两个线程都通过了外部的判空,进入synchronize入口处,此时由于同步加锁,只有一个线程可以执行synchronize内部的代码(生成了单例对象,当它执行完释放了锁后,第二个线程就进入的synchronize内部的代码,如果此时不再判断一下的话,该对象就再次被创建了。

  ⑽怎么实现一个线程安全的单例模式

  ⑾设计模式在面试中挺常考的,单例模式是考的最多的。线程安全的单例还有很多种实现方法。可以使用静态内部类、双重校验锁、静态代码块等。

  ⑿饿汉式public?class?Singleton(){????private?static?Singleton?instance?=?new?Singleton();????private?Singleton(){}????public?static?Singleton?getInstance(){????????return?instance;????}}、懒汉式public?class?Singleton(){????private?static?Singleton?instance;????private?Singleton({}????public?static?synchronized?Singleton?getInstance(){????????if(instance==null){????????????instance?=?new?Singleton();????????}????????return?instance;????}}

  ⒀boost库中的单例模式怎么使用

  ⒁单例模式大致有五种写法,分别为懒汉,恶汉,静态内部类,枚举和双重校验锁。、懒汉写法,常用写法classLazySingleton{privatestaticLazySingletonsingleton;privateLazySingleton(){}publicstaticLazySingletongetInstance(){if(singleton==null){singleton=newLazySingleton();}returnsingleton;}}、恶汉写法,缺点是没有达到lazyloading的效果classHungrySingleton{privatestaticHungrySingletonsingleton=newHungrySingleton();privateHungrySingleton(){}publicstaticHungrySingletongetInstance(){returnsingleton;}}、静态内部类,优点:加载时不会初始化静态变量INSTANCE,因为没有主动使用,达到LazyloadingclassInternalSingleton{privatestatilassSingletonHolder{privatefinalstaticInternalSingletonINSTANCE=newInternalSingleton();}privateInternalSingleton(){}publicstaticInternalSingletongetInstance(){returnSingletonHolder.INSTANCE;}}、枚举,优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象enumEnumSingleton{INSTANCE;publicvoiddoSomeThing(){}}、双重校验锁,在当前的内存模型中无效classLockSingleton{privatevolatilestaticLockSingletonsingleton;privateLockSingleton(){}//详见:publicstaticLockSingletongetInstance(){if(singleton==null){synchronized(LockSingleton.class){if(singleton==null){singleton=newLockSingleton();}}}returnsingleton;}}参考自:snippet__

  ⒂如何实现一个单例模式c#双重锁检查

  ⒃你好!以下是标准的SingletonpatternwithDouble-checklock。

  ⒄public?class?SingletonClass????{????????private?static?readonly?object?_lock?=?new?object();????????private?static?volatile?SingletonClass?_instance;????????public?static?SingletonClass?Instance????????{????????????get????????????{????????????????if?(_instance?==?null)????????????????{????????????????????lock?(_lock)????????????????????{????????????????????????if?(_instance?==?null)????????????????????????{????????????????????????????_instance?=?new?SingletonClass();????????????????????????}????????????????????}????????????????}????????????????return?_instance;????????????}????????}????????private?SingletonClass()????????{????????????//your?constructor????????}????}

  ⒅如果你的.在.以上,可以借助Lazy《T》泛型更优雅的实现单例模式:

  ⒆public?class?LazySingleton????{????????private?static?readonly?Lazy《LazySingleton》?_instance?=?new?Lazy《LazySingleton》(()?=》?new?LazySingleton());????????private?LazySingleton()????????{????????????//your?constructor????????}????????public?static?LazySingleton?Instance?{?get?{?return?_instance.Value;?}?}????}

  ⒇如何在java中实现singleton模式

  ⒈单例模式大致有五种写法,分别为懒汉,恶汉,静态内部类,枚举和双重校验锁。

  ⒉class?LazySingleton{????private?static?LazySingleton?singleton;????private?LazySingleton(){????}????public?static?LazySingleton?getInstance(){????????if(singleton==null){????????????singleton=new?LazySingleton();????????}????????return?singleton;????}???}

  ⒊恶汉写法,缺点是没有达到lazyloading的效果

  ⒋class?HungrySingleton{????private?static?HungrySingleton?singleton=new?HungrySingleton();????private?HungrySingleton(){}????public?static?HungrySingleton?getInstance(){????????return?singleton;????}}

  ⒌静态内部类,优点:加载时不会初始化静态变量INSTANCE,因为没有主动使用,达到Lazyloading

  ⒍class?InternalSingleton{????private?static?class?SingletonHolder{????????private?final?static??InternalSingleton?INSTANCE=new?InternalSingleton();????}???????private?InternalSingleton(){}????public?static?InternalSingleton?getInstance(){????????return?SingletonHolder.INSTANCE;????}}

  ⒎枚举,优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象

  ⒏enum?EnumSingleton{????INSTANCE;????public?void?doSomeThing(){????}}

  ⒐双重校验锁,在当前的内存模型中无效

  ⒑class?LockSingleton{????private?volatile?static?LockSingleton?singleton;????private?LockSingleton(){}????public?static?LockSingleton?getInstance(){????????if(singleton==null){????????????synchronized(LockSingleton.class){????????????????if(singleton==null){????????????????????singleton=new?LockSingleton();????????????????}????????????}????????}????????return?singleton;????}}

您可能感兴趣的文章:

相关文章