在一些文章中,它说使用volatile修复了Double Checked Locking问题。
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper(); //Important
}
}
return helper;
}
}
但是,即使我们使用volatile作为辅助字段,这怎么可能是一个安全的出版物呢? 我的意思是如何保证我们不会得到一个不一致的Helper对象?
答案 0 :(得分:3)
没有volatile,因为初始if (helper == null)
在同步块之外,所以没有可见性/一致性保证。特别是,helper
可能不是null,而是指仅部分创建的对象。
那是因为helper = new Helper()
不是原子操作:
helper
如果没有同步,观察线程可以按任何顺序看到这些操作,特别是它可以在2之前看到3。
通过使helper
为volatile,您在写入和读取之间引入了发生之前的关系(由the Java Memory Model定义),这可确保如果您看到3从观察线程中,您还将看到1和2。
特别是,在易失性写入之前执行的任何操作都将从易失性读取中可见(如果是后续的话)。
答案 1 :(得分:1)
我认为这是因为JVM保留了helper=new Helper()
的顺序。我的意思是只有在创建对象之后才会进行赋值。如果我错了,请纠正我。
答案 2 :(得分:0)
从高级别的角度来看,volatile
保证在易失性写入之前发生的任何操作的结果(即new Helper()
内的操作)对于任何其他线程都是可见的在后续的volatile读取之后(即当另一个线程在外部检查中看到helper != null
时)。
从低级别的角度来看,它依赖于实现。但总的来说:
答案 3 :(得分:-1)
好的,同步部分外的检查是为了避免不必要地进入监视器部分。如果您尝试优化性能,可以省略它。只有在对象的creaton之后无法将helper重置为null时,它才有效。同步部分内部的检查是为了避免不一致(因为我们在等待进入监视器时可能已经创建了对象)。
synchronized的内部部分只能相互独立执行。这就是一致性的来源。但是又一次:只有当helper在被赋予对Helper实例的新引用后才能变为null时,才允许外部if (helper == null)
。