为什么在Double-Checked-Locking中添加第二个测试?

Nas*_*LEK 1 java multithreading synchronization design-patterns

请参阅此https://en.wikipedia.org/wiki/Double-checked_locking,我们有:

// "Double-Checked Locking" idiom
class Foo {
    private Helper helper;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }    
    // other functions and members...
}
Run Code Online (Sandbox Code Playgroud)

第二次测试的目的是什么?2个线程是否真的可以同时访问同一个关键部分?

Nay*_*uki 5

两个线程不能同时访问同一个关键部分; 根据定义,关键部分是互斥的.

要回答您的问题,第一个null测试是没有同步的廉价测试,第二个null测试是通过同步检查实际状态.


第二次测试是必要的.假设我们没有它,代码看起来像这样:

public Helper getHelper() {
    if (helper == null) {
        synchronized(this) {
            helper = new Helper();
        }
    }
    return helper;
}
Run Code Online (Sandbox Code Playgroud)

假设线程A执行if (helper == null)并进行测试true,因此它进入if块但执行被暂停.没有变量更新.

然后线程B执行if (helper == null)并进行测试true,因此它进入if块.然后它继续执行到synchronized块中并初始化helper对象并返回.

现在,线程A继续执行,进入synchronized块,helper用新对象覆盖,然后返回对象.

我们遇到的问题是helper使用不同的对象进行了两次初始化.

这就是为什么第二次测试是必要的.

  • 只是为了避免有人简单地复制OP的代码:它不是100%正确**,[wiki](https://en.wikipedia.org/wiki/Double-checked_locking)说你必须将变量声明为"volatile"或使用[按需初始化习惯用法](https://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom)或包装器. (5认同)