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

14 浏览
0 Comments

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

据我所了解,这是Java中双重检查锁定模式(自Java 5以来)的正确实现:\n

class Foo {
    private volatile Bar _barInstance;
    public Bar getBar() {
        if (_barInstance == null) {
            synchronized(this) { // or synchronized(someLock)
                if (_barInstance == null) {
                    Bar newInstance = new Bar();
                    // 可能的额外初始化
                    _barInstance = newInstance;
                }
            }
        }
        return _barInstance;
    }
}

\n我想知道,如果没有使用`volatile`,是一个严重的错误还是仅仅是一个轻微的缺陷,假设`_barInstance`只通过`getBar`访问。\n我的观点是:`synchronized`引入了happens-before关系。初始化`_barInstance`的线程在离开同步块之前将其值写入主内存。因此,即使它没有使用`volatile`,也不会出现对`_barInstance`的重复初始化:其他线程在它们的本地副本中具有`null`(在第一次检查时返回`true`),但是在进入同步块后必须从主内存中读取新值(在第二次检查时返回`false`并且不进行重新初始化)。因此,唯一的问题是每个线程都会获取多余的一次锁。\n据我所了解,这在CLR中是正确的,我相信在JVM中也是正确的。我对吗?\n谢谢。

0
0 Comments

使用双重检查锁定(double-checked locking)模式时,若没有使用volatile关键字,可能会产生以下问题:

1. 线程1进入getBar()方法,发现_barInstance为null。

2. 线程1尝试创建一个Bar对象并更新_barInstance的引用。由于编译器的某些优化,这些操作可能会乱序执行。

3. 与此同时,线程2进入getBar()方法,看到_barInstance非null,但可能会看到_barInstance对象中的成员字段具有默认值。实际上,线程2看到的是一个部分构造的对象,但引用不为null。

为了解决这个问题,可以使用volatile修饰符来禁止对变量_barInstance的写入或读取与之前的读取或写入有关。这样,它可以确保线程2不会看到一个部分构造的对象。

更多详情请参考:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

0