据我所知,这是Java中双重检查锁定模式的正确实现(自Java 5起):
class Foo {
private volatile Bar _barInstance;
public Bar getBar() {
if (_barInstance == null) {
synchronized(this) { // or synchronized(someLock)
if (_barInstance == null) {
Bar newInstance = new Bar();
// possible additional initialization
_barInstance = newInstance;
}
}
}
return _barInstance;
}
}
我想知道volatile
的缺席是否是一个严重的错误,或者只是因为_barInstance
仅通过getBar
访问而导致可能性能下降的轻微不完。
我的想法如下:synchronized
介绍发生在之前的关系。初始化_barInstance
的线程将其值写入主存储器,而不是同步块。因此,即使_barInstance
不是volatile
,也不会进行双重初始化:其他帖子在null
的本地副本中有_barInstance
(获取true
在第一次检查中),但在进入同步块后,必须在第二次检查中从主存储器中读取新值(get false
并且不进行重新初始化)。所以唯一的问题是每次线程锁定获取过多。
据我了解,它在CLR中是正确的,我相信它在JVM中也是正确的。我是对的吗?
谢谢。
答案 0 :(得分:7)
在以下情况下,不使用volatile可能会导致错误:
getBar()
,找到_barInstance
为null
Bar
对象并更新对_barInstance
的引用。由于某些编译器优化,这些操作可能不按顺序进行。getBar()
并看到非空_barInstance
,但可能会在_barInstance
对象的成员字段中看到默认值。它基本上看到了部分构造的对象,但引用不是null。 volatile
修饰符将禁止相对于任何先前的读取或写入写入或读取变量_barInstance
。因此,它将确保线程2不会看到部分构造的对象。
有关详情:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html