ass*_*ias 42 java concurrency multithreading java-memory-model
下面的代码(Java Concurrency in Practice清单16.3)由于显而易见的原因而不是线程安全的:
public class UnsafeLazyInitialization {
private static Resource resource;
public static Resource getInstance() {
if (resource == null)
resource = new Resource(); // unsafe publication
return resource;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,几页之后,在第16.3节中,他们指出:
UnsafeLazyInitialization
如果Resource
是不可变的,实际上是安全的.
我不明白这句话:
Resource
是不可变的,那么观察resource
变量的任何线程都将看到它为null或完全构造(由于Java内存模型提供的最终字段的强保证)resource
可以重新排序(在一个读取if
和一个读取return
).因此,线程可以resource
在if
条件中看到非null 但返回空引用(*).我认为UnsafeLazyInitialization.getInstance()
即使Resource
是不可变的也可以返回null .是这样的,为什么(或为什么不)?
(*)为了更好地理解我关于重新排序的观点,Jeremy Manson的博客文章是JLS并发的第17章的作者之一,解释了如何通过良性数据竞争安全地发布String的哈希码以及如何删除使用局部变量可能导致哈希码错误地返回0,因为可能的重新排序非常类似于我上面描述的:
我在这里做的是添加一个额外的读取:在返回之前的第二次读取哈希.听起来很奇怪,并且不太可能发生,第一次读取可以返回正确计算的散列值,第二次读取可以返回0!这在内存模型下是允许的,因为该模型允许对操作进行大量重新排序.第二次读取实际上可以在您的代码中移动,以便您的处理器在第一次读取之前完成!
我认为您在这里感到困惑的是作者所说的安全出版的含义。他指的是非空资源的安全发布,但您似乎明白了。
您的问题很有趣 - 是否可以返回资源的空缓存值?
是的。
允许编译器像这样重新排序操作
public static Resource getInstance(){
Resource reordered = resource;
if(resource != null){
return reordered;
}
return (resource = new Resource());
}
Run Code Online (Sandbox Code Playgroud)
这并不违反顺序一致性规则,但可能返回空值。
这是否是最好的实现还有待争论,但没有规则来阻止这种类型的重新排序。
归档时间: |
|
查看次数: |
2545 次 |
最近记录: |