当两个线程同时调用“getInstance()”时,Singleton如何表现?

当两个线程同时调用“getInstance()”时,Singleton如何表现? 保护它的最佳做法是什么?

首先,两个线程不能在“同一时间”调用该方法 – 一个将被视为首先调用它…称为“竞争条件”。

接下来,任何正确实现的单例都将干净地处理竞争条件。 恕我直言,这是在没有同步的情况下实现线程安全单例的最简洁方法:

 public class MySingleton { private static class Holder { static final MySingleton INSTANCE = new MySingleton (); } public static MySingleton getInstance() { return Holder.INSTANCE; } // rest of class omitted } 

这称为按需初始化持有者习惯用法 。

如果在单例上使用延迟初始化,这只是一个问题。 如果您使用急切初始化,那么JVM会保证为您排序。

对于延迟初始化,您需要同步(尽管您可以使其变为volatile并使用双重检查锁定以避免始终使用同步块)或将其嵌入到内部类中,而不是在其中进行惰性初始化。

peter.petrov的答案现在很好地涵盖了大多数选项,有一种最后的方法来进行线程安全延迟初始化虽然没有涵盖,但它可能是最好的。

 public class Singleton { // Prevent anyone else creating me as I'm a singleton private Singleton() { } // Hold the reference to the singleton instance inside a static inner class private static class SingletonHolder { static Singleton instance = new Singleton(); } // Return the reference from inside the inner class public static Singleton getInstance() { return SingletonHolder.instance; } } 

Java在类上进行延迟加载,它们仅在首次访问时加载。 这也适用于内部类……

1)如果你想要延迟初始化,我认为一个好的做法是在私有静态最终的Object实例上同步getInstance主体,该实例是同一个类的成员(例如,你可以将它命名为LOCK)。

2)如果你不需要lazy init,你可以在类加载时实例化你的单例实例。 然后在getInstance中不需要任何同步。

1)样品,不使用DCL(双重锁定)

注1:这个通过在性能方面支付一些额外的价格来避免使用DCL的复杂性。
注2:此版本在JDK <5以及JDK> = 5时正常。

 public class Singleton { private static final Object LOCK = new Object(); private static Singleton instance = null; public static Singleton getInstance(){ synchronized(LOCK){ if (instance == null){ instance = new Singleton(); } return instance; } } private Singleton(){ // code to init this } } 

1)使用DCL的样品

注1:这在JDK> = 5上是正常的,但在JDK <5时没有。
注2:注意使用的volatile关键字,这很重要。

 public class Singleton { private static final Object LOCK = new Object(); private static volatile Singleton instance = null; public static Singleton getInstance(){ if (instance == null){ synchronized(LOCK){ if (instance == null){ instance = new Singleton(); } } } return instance; } private Singleton(){ // code to init this } } 

样本2)

注1:这是最简单的版本。
注2:适用于任何JDK版本。

 public class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } private Singleton(){ // code to init this } } 

参考文献:

1)较旧的JDK版本(JDK <5)
http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking–clever–but-broken.html

2)DCL的最新更新
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

 public class Singleton{ private static class Holder { static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } } 

当第一次调用getInstance方法时,它首次读取Holder.INSTANCE,导致Holder类被初始化。 这个习惯用法的优点在于getInstance方法不是同步的,只执行字段访问。这称为延迟初始化。 您可能认为加载Singleton类时,类加载器也应该加载Holder类,因为Holder类是静态的。 加载顶级类不会自动加载任何嵌套类型,除非在顶级初始化期间发生了一些其他初始化,例如,如果您的顶级类具有需要通过引用初始化的静态字段嵌套类的实例。

同步getInstance的访问权限。

Class TestSingleton {

  private static volatile TestSingleton singletonObj = null; private TestSingleton (){ // make constructor private } public static getInstance(){ TestSingleton obj = singletonObj ; if(obj == null) { synchronized(lock) { // while we were waiting for the lock, another obj = instance; // thread may have instantiated the object if(obj == null) { obj = new TestSingleton(); instance = obj ; } } } public doSomeWork(){ // implementation } 

}