java最新的JMM是否将同步块指定为其他线程(甚至异步线程)的原子块?
当我阅读一篇关于http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking--clever--but-broken.html上的双重检查锁定的文章时,我遇到了一条评论:“应该注意的是,DCL实际上可以在某些JVM的某些版本上工作——因为很少有JVM真正正确地实现JMM。” 因此,我推断JMM将同步块指定为原子块,即使是在其他线程中未同步的块。 我说得对吗?(我试图阅读甲骨文网站上的JMM,但它太抽象了,我放弃了。)
# 1 楼答案
首先,请注意Brian Goetz在2001年写了这篇文章。本文中描述的信息在implementation of JSR-133之后不再准确,这是一种修正的记忆模型。然而,本文中的示例DCL被破坏了,这是事实:
使用上述代码,当实例的构造函数尚未完全执行时,
resource
字段可能会被观察到不是null
。问题是,由于JVM可以应用代码优化,因此不能保证在字段分配之前执行构造函数。因此,构造函数调用应该被视为(在伪代码中):有了这些信息,我们可以看到初始检查
resource == null
如何为另一个线程生成false
,甚至在new
被称为将不完整的实例暴露给另一个线程之前。另一个线程永远不会进入同步块,也不会等待构造函数调用完成然而,在今天的Java中,将
resource
字段设为volatile
就足够了。在这种情况下,DCL确实有效,甚至相当有效,因为读取易失性字段是not too expensive on most hardware。Alexey Shipilev讨论了安全、懒惰的发布in detail对性能的影响。带有volatile
的DCL是当今常见的模式,例如Scala使用它的lazy
字段但要回答您的实际问题:基本上,JVM的所有实现都以比其规范更宽松的方式实现内存模型。因此,非易失性DCL可能只在许多机器上工作,尽管由于实现细节而导致了不正确的同步。但是,您永远不应该针对实现编写代码,而应该始终针对规范编写代码。否则,您的代码可能只会偶尔失败,而且只会在某些机器上失败,这是一个很可怕的错误!这与
synchronized
块的原子性无关,它只与VM如何执行代码有关,在将实例发布到resource
字段之前,构造函数可能会顺便执行