volatile在java中的单例双空检查实现中的使用

volatile在java中的单例双空检查实现中的使用,java,multithreading,singleton,volatile,Java,Multithreading,Singleton,Volatile,我想知道在Singleton的双空检查实现中将实例变量设置为volatile有什么用。因为根据我的理解,同步块提供之前是隐式发生的。没有两个线程可以同时访问该同步块,并且在退出同步块时,线程会将其所有本地缓存数据写入主内存。 我搜索了很多,但仍然对这个实现有疑问。请解释正确的用法 private volatile Singleton INSTANCE; public Singleton get() { if (INSTANCE == null) { synchron

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

private volatile Singleton INSTANCE; 
public Singleton get() { 
    if (INSTANCE == null) { 
        synchronized (this) { 
            if (INSTANCE == null) { 
                INSTANCE = new Singleton(); 
            } 
        } 
    } 
    return INSTANCE; 
}
在上面的双重检查代码中。如果我不让我的实例变得易变,那么会有什么问题呢。因为当第一个线程进入同步块时,其他线程将无法访问该块。当第一条线离开那个块时。对象将被创建,并且由于发生在属性之前,实例的最新值将写入主内存。那么为什么volatile在这种情况下很重要呢

这里有一个类似问题的链接()。但答案并不十分清楚。 我不认为在上面给定的场景中,任何类型的JVM优化都会导致在同步块之前发生的中断

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

如果您有一个简单的惰性单例实现,这是正确的。在这种情况下,对变量的每次访问都要经过syncronized块

private Singleton INSTANCE;

public static synchronized Singleton getInstance() {
  if (INSTANCE == null) {
    INSTANCE = new Singleton();
  }
  return INSTANCE;
}
但一旦您有了一个安全的双重检查延迟实现,并不是每个代码路径都经过同步块。对该方法的第二次调用将从本地字段读取变量,而无需输入
synchronized
块,
实例必须是可变的

private volatile Singleton INSTANCE;

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

您可以在中阅读有关安全发布的更多信息

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

INSTANCE = new Singleton()
在字节码级别实际上是这样的:

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

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

向我们展示您正在谈论的代码,并准确地向我们展示在您谈论之前发生的事情。然后我们可以解释它。(这可能是一个副本,尽管没有更多细节,很难选择最佳的dup。)请将其放入问题中。这是一个副本,感谢您澄清volatile仅用于双重检查锁定单例模式,并给出正确的原因“实例变量的第一次检查是在不进入同步块的情况下检查null”。