带有两个同步块的 DCL 坏了吗?

DCL with two synchronized block is broken?

我无法理解 A fix that doesn't work 中的以下代码片段。 (我确实阅读了同一页上的解释)。

如果我们有2个同步块,这个DCL版本是怎么坏的?还是不适用postJava5?

// (Still) Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) {
      Helper h;
      synchronized(this) {
        h = helper;
        if (h == null) 
            synchronized (this) {
              h = new Helper();
            } // release inner synchronization lock
        helper = h;
        } 
      }    
    return helper;
    }
  // other functions and members...
  }

无法保证将 helper 视为非空的线程将能够看到 new Helper(); 进行的所有写入。所以你可以访问单例的损坏版本。您需要在将 helper 视为非 null 的线程中确保它看到 after h = new Helper(); 完成。观察对非易失性变量的更改并不能建立这种关系,这就是线程所做的一切。

过于简单化了,Java 的内存可见性模型的工作方式是两个线程各自做一些事情,在两个操作之间建立 "happens before" / "happens after" 关系两个线程。这可以包括同步块内的操作或对 volatile 变量的访问。

但是使用上面的代码,线程可以观察到 helper 不是 null,然后继续访问 new Helper() 创建的对象。它不必访问 volatile 变量,也不必进入同步块。所以没有什么可以建立所需的 "happens after" 关系来确保它看到 new Helper().

所做的任何更改