java - Java 双重检查锁定中的 volatile

标签 java multithreading

<分区>

据我所知,这是 Java 中双重检查锁定模式的正确实现(自 Java 5 起):

class Foo {
    private volatile Bar _barInstance;
    public Bar getBar() {
        if (_barInstance == null) {
            synchronized(this) { // or synchronized(someLock)
                if (_barInstance == null) {
                    Bar newInstance = new Bar();
                    // possible additional initialization
                    _barInstance = newInstance;
                }
            }
        }
        return _barInstance;
    }
}

我想知道如果 _barInstance 仅通过 getBar 访问,那么缺少 volatile 是否是一个严重的错误或只是一个可能存在性能缺陷的轻微缺陷。

我的想法如下:synchronized 引入了happens-before 关系。初始化 _barInstance 的线程将其值写入主内存,离开同步块(synchronized block)。因此,即使 _barInstance 不是 volatile,也不会对其进行双重初始化:其他线程在其 的本地副本中具有 null _barInstance(第一次检查得到true),进入synchronized block 后第二次检查必须从主存中读取新值(get false 并且不进行重新初始化)。因此,唯一的问题是过度获取每个线程一个锁。

据我所知,它在 CLR 中是正确的,我相信它在 JVM 中也是正确的。我说得对吗?

谢谢。

最佳答案

在以下情况下,不使用 volatile 可能会导致错误:

  • 线程1进入getBar(),发现_barInstancenull
  • 线程 1 尝试创建一个 Bar 对象并更新对 _barInstance 的引用。由于某些编译器优化,这些操作可能会乱序执行。
  • 与此同时,线程 2 进入 getBar() 并看到一个非空的 _barInstance 但可能会在 _barInstance 的成员字段中看到默认值目的。它本质上看到了一个部分构造的对象,但引用不为空。

volatile 修饰符将禁止对变量 _barInstance 的任何先前读取或写入操作进行写入或读取。因此,它将确保线程 2 不会看到部分构造的对象。

更多详情:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

关于java - Java 双重检查锁定中的 volatile,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28034564/

相关文章:

java - 当两个单独的应用程序使用同一个数据库时,Hibernate 一级缓存结果是否会过时?

java - 无法转换为 org.apache.spark.serializer.Serializer

java - 我们什么时候在处理 'GET' 的 java 方法上使用 Synchronized ?

multithreading - 你如何杀死一个 core.async/thread?

Java 重新排序和不稳定问题

c++ - 使用 LLVM 在 JXcore 中可用的 Node 插件

java - Java 中的定时过期类和任务

java - 当我尝试无休止地更新 JTextField 时,为什么我的程序会卡住?

java - Android:在网格中显示文本的简单 GridView

java - 如何通过不同Java类中的方法更改对象值