繁体   English   中英

Java对双重检查锁定的同步影响?

[英]Java synchronized effect on Double-checked locking?

我已经阅读了不同的文章,例如“ 双重检查锁定”:聪明,但已损坏,并且我理解以下代码在多线程用法中被损坏的原因。

class SomeClass {
  private Resource resource = null;
  public Resource getResource() {
    if (resource == null) {
      synchronized {
        if (resource == null) 
          resource = new Resource();
      }
    }
    return resource;
  }
}

但是,根据其解释,当线程退出同步块时,它将执行写屏障-在释放锁之前,它必须将在该块中修改的所有变量清除到主内存中。 因此,当线程A运行到同步块中,然后按顺序执行以下过程时:

  1. 新资源对象的内存将被分配;
  2. Resource的构造函数将被调用,
  3. 初始化新对象的成员字段;
  4. SomeClass的字段资源将被分配一个对新创建对象的引用

最后,在线程A从同步块退出之前,它将线程的本地资源对象写回到主内存,然后线程B一旦运行到同步块,就会从主存储器读取此新创建的资源。

为什么线程B看到这些内存操作的顺序与线程A执行顺序不同? 我以为线程B在从同步块退出时直到线程A将其本地内存刷新到主内存之前才知道资源对象的创建,因为线程B只能从可共享主内存中读取资源对象?

请更正我的理解。。谢谢。

您引用的文章引用了Java 5.0之前的Java内存模型。

在Java 5.0+中,必须将您的resource声明为volatile才能起作用。 即使更改已刷新到主内存中,也不能保证(除了volatile )线程B将从主内存中读取新值,而不是从其自己的本地缓存中读取(该值为null)。

在以前的版本中,volatile没有对重新排序施加严格的限制,因此不能保证经过双重检查的锁定都能正常工作。

是“双重检查锁定”不会消失的模因之一。 使用枚举的IMHO更加智能(如Josh Bloch在Effective Java 2nd Edition中建议的那样)

enum SomeClass {
    INSTANCE; // thread safe and lazy loaded
}

您所指的错误已于2004年在Java 5.0中修复。

简而言之,a)不使用它b)使用Java 5.0+版本c)不要使用不很旧的不受支持的Java版本,而要花大量的篇幅来撰写非常老的文章(2001年)。

最后,在线程A从同步块退出之前,它将线程的本地资源对象写回到主内存,然后线程B一旦运行到同步块,就会从主存储器读取此新创建的资源。

这是它分解的地方。 由于线程B在不同步的情况下访问resource ,因此对其操作没有读取障碍。 因此,它可能会看到该resource单元或对应于该Resource实例的某个字段的单元的存储器的过时缓存副本。

Costi Ciudatu的修复程序对于Java版本> = 5.0是正确的。 但是对于早于该版本的版本, volatile的语义不能保证所有更改都将从A刷新到主内存再到B。

我不想再说其他人已经做过的事情了,但是由于这是一个经常使用的模式,为什么不仅仅为此做一个实用方法呢? 喜欢: 供应商记忆

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM