有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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


共 (1) 个答案

  1. # 1 楼答案

    首先,请注意Brian Goetz在2001年写了这篇文章。本文中描述的信息在implementation of 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,甚至在new被称为将不完整的实例暴露给另一个线程之前。另一个线程永远不会进入同步块,也不会等待构造函数调用完成

    然而,在今天的Java中,将resource字段设为volatile就足够了。在这种情况下,DCL确实有效,甚至相当有效,因为读取易失性字段是not too expensive on most hardware。Alexey Shipilev讨论了安全、懒惰的发布in detail对性能的影响。带有volatile的DCL是当今常见的模式,例如Scala使用它的lazy字段

    但要回答您的实际问题:基本上,JVM的所有实现都以比其规范更宽松的方式实现内存模型。因此,非易失性DCL可能只在许多机器上工作,尽管由于实现细节而导致了不正确的同步。但是,您永远不应该针对实现编写代码,而应该始终针对规范编写代码。否则,您的代码可能只会偶尔失败,而且只会在某些机器上失败,这是一个很可怕的错误!这与synchronized块的原子性无关,它只与VM如何执行代码有关,在将实例发布到resource字段之前,构造函数可能会顺便执行