我在一些开源代码中发现了这段代码,我发现这些代码有不稳定的行为,应将其重命名为未命名(名称已更改)。我很确定它不是线程安全的,因为该资源缺少 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/