在java中使用volatile中的singleton double null check实现

时间:2017-01-28 05:19:47

标签: java multithreading singleton volatile

我想知道在Singleton的double null check实现中将Instance变量设置为volatile的用途。因为根据我的理解,同步块提供在隐式之前发生。没有两个线程可以同时访问该同步块,并且在从synchronized块退出时,线程将其所有本地缓存​​数据写入主内存。 我搜索了很多但仍然对这个实现有疑问。请解释正确使用。

private volatile Singleton INSTANCE; 
public Singleton get() { 
    if (INSTANCE == null) { 
        synchronized (this) { 
            if (INSTANCE == null) { 
                INSTANCE = new Singleton(); 
            } 
        } 
    } 
    return INSTANCE; 
}

在上面的双重检查代码中。如果我不使我的实例变得不稳定,那么问题可能是什么。因为当第一个线程进入同步块时,没有其他线程可以访问该块。当第一个线程离开时阻塞。将创建对象,并且由于在属性之前发生,实例的最新值将写入主存储器中。那么为什么volatile在这种情况下很重要呢?

这是类似问题的链接(Is there any need to add volatile keyword to guarantee thread-safe singleton class in java?)。但答案不是很清楚。 我不认为在上面给出的情况下,任何类型的JVM优化都可能导致在同步块之前发生中断

2 个答案:

答案 0 :(得分:1)

  

因为根据我的理解,同步块提供在隐式之前发生。没有两个线程可以同时访问同步块并从同步块

退出

如果你有一个简单的懒惰单例实现,这是正确的。因为每次访问变量都会通过同步块。

private Singleton INSTANCE;

public static synchronized Singleton getInstance() {
  if (INSTANCE == null) {
    INSTANCE = new Singleton();
  }
  return INSTANCE;
}

但是一旦你有一个安全的仔细检查延迟实现,并不是每个代码路径都通过synchronized块。第二次调用此方法将从本地字段读取变量而不输入synchronized块,INSTANCE必须是易失性的。

private volatile Singleton INSTANCE;

public Singleton get() {
  if (INSTANCE == null) {
    synchronized (this) {
      if (INSTANCE == null) {
        INSTANCE = new Singleton();
      }
    }
  }
  return INSTANCE;
}

您可以在this article中详细了解安全发布。

答案 1 :(得分:0)

volatile在这里很重要,因为字节码级别上的JVM内部。声明

INSTANCE = new Singleton()

在字节码级别实际上是这样的:

1. INSTANCE = create Singleton object
2. call constructor of INSTANCE

如果INSTANCE为volatile,则JVM保证从其他线程的角度来看,1 + 2作为原子操作执行,例如通过将实例存储在堆栈中,然后将其存储在字段中。如果不是 volatile,可能是其他线程在调用构造函数之前已经看到对Singleton的引用。因此,这些线程可能会看到一个不完整的对象。