当我阅读一篇关于http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking--clever--but-broken.html上的双重检查锁定的文章时,我遇到一条评论:“应该注意的是,DCL实际上可以在某些JMM的版本上工作--因为很少有JMM真正正确地实现了JMM。”因此,我由此推断,JMM将同步块指定为原子块,即使是在其他线程中未同步的块。我说的对吗?(我试着在甲骨文的网站上阅读JMM,但它太抽象了,于是我放弃了。)
发布于 2015-12-24 15:37:04
首先,请注意Brian在2001年写了这篇文章。本文描述的信息在修改后的内存模型执行JSR-133之后不再准确。然而,本文的示例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 = alloc Resource;
resource.new();有了这些信息,您就可以看到初始检查resource == null如何在调用false之前为另一个线程生成false,而new将不完整的实例公开给另一个线程。另一个线程将永远不会进入同步块,而不会等待构造函数调用的完成。
然而,在当今的Java中,将resource字段设置为volatile就足够了。在这种情况下,DCL确实工作,甚至非常高效,因为读取易失性字段是大多数硬件不太贵。Alexey讨论了安全、懒惰的出版物详述的性能含义。带volatile的DCL是当今常见的模式,例如Scala在其lazy字段中使用。
但要回答您的实际问题:基本上,JVM的所有实现都以比其规范更宽松的方式实现内存模型。因此,非易失性DCL可能只在许多机器上工作,尽管由于实现细节而存在不正确的同步。但是,您不应该针对实现编写代码,而应该始终违背规范。否则,您的代码可能只会在某些机器上失败--这是一个可怕的错误跟踪!这与incidentally块是原子性无关,它只与VM如何执行代码有关,在此代码中,在将实例发布到resource字段之前,可能总是执行构造函数。
https://stackoverflow.com/questions/34450511
复制相似问题