2

我正在查看我们应用程序中的一些代码,我认为这些代码可能会遇到“双重检查锁定”的情况。我已经编写了一些与我们所做的类似的示例代码。

谁能看到这是如何经历双重检查锁定的?或者这样安全吗?

class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        Helper result;
        synchronized(this) {
            result = helper;
        }

        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }
}

从wiki借来的基本代码。

4

3 回答 3

6

这是不必要的复杂,做 DCL 的最简单的“安全”方法是这样的:

class Foo {
  private volatile Helper helper = null;
  private final Object mutex = new Object(); 
  public Helper getHelper() {
    if (helper == null) {
        synchronized(mutex) {
            if (helper == null) {
                helper = new Helper();
            }
        }
    }
    return helper;
  }
}

这里的关键点是:

  • 在“快乐”的情况下,我们希望 helper 已经被分配,所以如果是,我们可以直接返回它而无需进入同步块。
  • Helper 被标记为 volatile 以让编译器知道 helper 可以随时被任何线程读取/写入,并且重要的是读取/写入不会重新排序。
  • 同步块使用私有 final 变量进行同步,以避免在实例上另一个代码区域同步的情况下潜在的性能损失this
于 2011-12-06T23:01:51.363 回答
3

双重检查锁定的重点是快速路径(当您不需要实例化对象时)不同步。所以你所拥有的不是双重检查锁定。

您需要摆脱第一个同步块才能获得损坏的双重检查锁。然后你需要做helpervolatile 来修复它。

于 2011-12-06T23:03:44.677 回答
0

这部分是标准的双重检查锁定:

if (helper == null) {
    synchronized(this) {
        if (helper == null) {
            helper = new Helper();
        }
    }
}

第一部分是一个无用的赋值,它对双重检查锁定部分没有任何作用:如果 helper 为 null 则无论如何都会执行,如果不是则无论如何都不会执行。这是完全无效的。

于 2011-12-06T23:05:01.513 回答