我在一些开源代码中发现了这段代码,我一直看到它的行为不稳定,应该重命名为未命名的(名称已被更改)。我很确定它不是线程安全的,因为资源缺少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
答案 0 :(得分:2)
不,它不安全 - 但由于通常的双重检查锁定问题,而不是因为第二种方法调用。具体来说,第二个线程可以看到一个非null defaultResource
,从而在没有看到synchronized方法的情况下访问它,因此不会在第一个线程上调用它之前建立一个before-before边缘;但这与内联的synchronized (this)
块完全相同。
你对这种动荡也是正确的。如果该字段是易变的,那么读取它将在之前的写入之前建立一个发生之前。
方法与Java内存模型(JMM)无关 - 除非它们已经同步,在这种情况下它们的效果与synchronized
块完全相同。我不鼓励考虑方法,而是鼓励你考虑具体的行动:什么行动建立了先发生的优势?在您提供的代码中,可以提供先发生优先级的唯一操作是在同一监视器的先前版本之后(由另一个监视器获取this
监视器(通过输入同步方法)线程退出该方法)。那么你必须要问:我们有没有办法在没有经过这些行动的情况下读取非空defaultResource
?答案是肯定的:实际上,defaultResource
在getSession
开始时${variant.productFlavors[0].name}
处于非空状态。因此,这些访问中的每一个都是数据竞争。