我想知道在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优化都可能导致在同步块之前发生中断
答案 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的引用。因此,这些线程可能会看到一个不完整的对象。