为什么我们在Singleton延迟初始化中检查两次null?

时间:2013-04-14 03:53:44

标签: java singleton

我在Wikipedia上针对Singleton模式遇到了这段代码。任何人都可以解释两次检查null的目的/逻辑吗?

public class SingletonDemo {
    private static volatile SingletonDemo instance = null;

    private SingletonDemo() {       }

    public static SingletonDemo getInstance() {
            if (instance == null) {
                    synchronized (SingletonDemo .class){
                            if (instance == null) {
                                    instance = new SingletonDemo ();
                            }
                  }
            }
            return instance;
    }
  }

5 个答案:

答案 0 :(得分:2)

来自维基百科链接的引用:

  

此方法使用双重检查锁定

http://en.wikipedia.org/wiki/Double_checked_locking_pattern#Usage_in_Java

答案 1 :(得分:1)

最好使用内部类进行延迟初始化而不是“双重检查锁定”:

public class Singleton {
  // Private constructor prevents instantiation from other classes
  private Singleton() {}

  /**
   * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
   * or the first access to SingletonHolder.INSTANCE, not before.
   */
  private static class SingletonHolder { 
    private static final Singleton INSTANCE = new Singleton();
  }

  public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}

双重检查锁定并非万无一失。 JVM保证静态内部类可以懒惰地创建线程安全的Singleton类。

答案 2 :(得分:0)

假设有两个线程T1和T2调用相同的方法。

现在T1通过第一次检查,发现实例为空,然后进入睡眠状态。让我们说T2开始(可能不会一直发生,但肯定会在某些时候发生),然后通过第一次检查并发现该对象为空,获取锁并创建对象。

现在T1醒来并获得锁定。此时,如果我们不再检查null,T1将永远不会知道T2已经创建了一个对象。因此,仔细检查。

您现在可能会问,为什么不在开头进行同步和空检查?如果我们一直同步检查null,那么这个方法一次只能访问单个线程,并导致性能问题,代价是什么?只是为了在程序开始时有一些效率,当只有2或3个线程试图创建该实例时。

答案 3 :(得分:0)

getInstance仍为instance时,多个线程可能会调用null

只有一个线程进入同步块(比方说T1),其他线程将等待。

当T1退出同步块时,其他一些线程将进入。

然后,如果没有if (instance == null),则会创建Singleton的新实例。

答案 4 :(得分:0)

此代码假定并发(同时使用该类的多个线程)。

代码首先尝试查看是否有实例。如果没有,则尝试创建一个。为避免其他线程在执行此操作的同时执行此操作,它会使用synchronized阻止代码。

现在,在第一个if (instance == null)synchronized之间,另一个更快的线程可能已经挤进并创建了实例,因此代码再次检查以确保。