Java中的双重检查锁定中的volatile

时间:2015-01-19 22:25:01

标签: java multithreading

据我所知,这是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中也是正确的。我是对的吗?

谢谢。

1 个答案:

答案 0 :(得分:7)

在以下情况下,不使用volatile可能会导致错误:

  • 主题1输入getBar(),找到_barInstancenull
  • 线程1尝试创建Bar对象并更新对_barInstance的引用。由于某些编译器优化,这些操作可能不按顺序进行。
  • 同时,线程2输入getBar()并看到非空_barInstance,但可能会在_barInstance对象的成员字段中看到默认值。它基本上看到了部分构造的对象,但引用不是null。

volatile修饰符将禁止相对于任何先前的读取或写入写入或读取变量_barInstance。因此,它将确保线程2不会看到部分构造的对象。

有关详情:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html