0

我在Wikipedia上偶然发现了此代码以获取单例模式。谁能解释两次检查 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;
    }
  }
4

5 回答 5

2

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

此方法使用双重检查锁定

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

于 2013-04-14T03:58:11.487 回答
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 类。

于 2013-04-14T04:06:54.530 回答
0

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

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

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

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

于 2013-04-14T03:59:52.810 回答
0

getInstanceinstanceis still时,可能会调用多个线程null

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

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

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

于 2013-04-14T04:00:00.873 回答
0

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

代码首先尝试查看是否存在实例。如果没有,则尝试创建一个。为了避免其他线程同时执行它,它使用synchronized.

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

于 2013-04-14T04:00:10.993 回答