Java 最新的JMM是否将同步块指定为其他线程(甚至异步线程)的原子块?

Java 最新的JMM是否将同步块指定为其他线程(甚至异步线程)的原子块?,java,multithreading,java-memory-model,double-checked-locking,synchronized-block,Java,Multithreading,Java Memory Model,Double Checked Locking,Synchronized Block,当我阅读一篇关于双重检查锁定的文章时,我遇到了一条评论:“应该注意的是,DCL实际上可能在某些JVM的某些版本上工作——因为很少有JVM真正正确地实现了JMM。” 因此,我推断JMM将同步块指定为原子块,即使是在其他线程中未同步的块。 我说得对吗?(我试图在oracle网站上阅读JMM,但它太抽象了,我放弃了。)首先,请注意Brian Goetz在2001年写了这篇文章。经过修正后的记忆模型,本文描述的信息不再准确。然而,本文中的示例DCL是不正确的: class SomeClass {

当我阅读一篇关于双重检查锁定的文章时,我遇到了一条评论:“应该注意的是,DCL实际上可能在某些JVM的某些版本上工作——因为很少有JVM真正正确地实现了JMM。” 因此,我推断JMM将同步块指定为原子块,即使是在其他线程中未同步的块。
我说得对吗?(我试图在oracle网站上阅读JMM,但它太抽象了,我放弃了。)

首先,请注意Brian Goetz在2001年写了这篇文章。经过修正后的记忆模型,本文描述的信息不再准确。然而,本文中的示例DCL是不正确的:

class SomeClass {

  private Resource resource = null;

  public Resource getResource() {
    if (resource == null) {
      synchronized (this) {
        if (resource == null) 
          resource = new Resource();
      }
    }
    return resource;
  }
}
使用上述代码,当实例的构造函数尚未完全执行时,
resource
字段可能被观察到不是
null
。问题是,由于JVM可以应用代码优化,因此不能保证在字段分配之前执行构造函数。因此,构造函数调用应该被视为(在伪代码中):

有了这些信息,我们可以看到初始检查
resource==null
如何在调用
new
之前为另一个线程生成
false
,从而将不完整的实例暴露给另一个线程。另一个线程永远不会进入同步块,也不会等待构造函数调用完成

然而,在今天的Java中,将
资源
字段设置为
易失性
就足够了。在这种情况下,DCL确实可以工作,甚至非常有效,因为读取易失性字段是非常困难的。Alexey Shipilev讨论了安全、懒惰发布的性能影响。带有
volatile
的DCL是当今常见的模式,例如Scala使用它的
惰性
字段


但要回答您的实际问题:基本上,JVM的所有实现都以比其规范更宽松的方式实现内存模型。因此,非易失性DCL可能只在许多机器上工作,尽管由于实现细节的原因,同步不正确。但是,您不应该针对实现编写代码,而应该始终针对规范编写代码。否则,您的代码可能只会在某些机器上有时失败,这是一个很可怕的错误!这与
synchronized
块的原子性无关,它只与VM如何执行代码有关,在将实例发布到
resource
字段之前,构造函数可能会顺便执行,我不明白你会如何从那评论中推断出来。不,你错了。如果您对此有疑问,请阅读JMM规范。任何人都可以提出一些毫无根据的猜测,但如果你没有读到你正在谈论的东西,你就不能指望人们深入反驳
resource = alloc Resource;
resource.new();