java - 如果初始化程序代码在方法之间拆分,双重检查锁惯用语是否安全?

标签 java multithreading locking synchronized

我在一些开源代码中发现了这段代码,我发现这些代码有不稳定的行为,应将其重命名为未命名(名称已更改)。我很确定它不是线程安全的,因为该资源缺少 volatile 关键字。

另外,我不确定这个习惯用法在按原样拆分时是否有效。 Josh Bloch claims这个习惯用法很脆弱,应该按照记录来实现,否则它可能不起作用(尽管他没有解释原因)。再加上双写不闻写。

除了这里缺少 volatile 之外,这段代码在多线程应用程序中可能会失败吗?

private Resource defaultResource = null;

public Resource getSession() {
    if (defaultResource == null) {
        defaultResource = initializeResource();
    }
    return defaultResource;
}

private synchronized Resource initializeResource() {
    if (defaultResource == null) {
        defaultResource = getResourceBuilder().build();
    }
    return defaultResource;
}

编辑:使用 Java 8

最佳答案

不,它不安全——而是因为通常的双重检查锁问题,而不是因为第二个方法调用。具体来说,第二个线程可以看到非空的 defaultResource,从而在没有看到同步方法的情况下访问它,因此不会在第一个线程上调用它时建立发生前边缘;但这与内联同步(this) block 的问题完全相同。

你对 volatile 的看法也是对的。如果该字段是 volatile 的,那么读取它会与其之前的写入建立一个发生之前。

方法与 Java 内存模型 (JMM) 无关 - 除非它们是同步的,在这种情况下,它们的效果与 synchronized block 完全相同。我鼓励您考虑具体的操作,而不是考虑方法:哪些操作建立了发生在边缘之前?在您提供的代码中,可以提供发生之前边缘的唯一操作是在同一监视器的先前版本(由另一个监视器)之后获取 this 的监视器(通过输入同步方法)线程退出该方法)。那么你必须问:有什么方法可以让我们在不执行这些操作的情况下读取非 null defaultResource 吗?答案是肯定的:事实上,只要 getSession 开头的 defaultResource 为非空,就会执行此操作。因此,每一次访问都是一场数据竞争。

关于java - 如果初始化程序代码在方法之间拆分,双重检查锁惯用语是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36192187/

相关文章:

Java 对象到 Hbase

java - 将关闭信号发送到Java中的另一个线程

c# - 独占锁与线程纤程

java - 锁在 API 中等待,以防暴露

java - 解释 jstack 跟踪以追踪停滞的锁

java - tomcat实例之间的通信(分布式架构)

java - Tomcat 中的 Http Session 生命周期

java - java/eclipse中的数据断点

Java 线程工作组性能与嵌入式循环性能

C# 从 backgroundworker 连续更新 GUI