问题描述
对接受的答案进行评论
这个问题产生的热量比我想象中的要多得多。一个重要的结论是,我从公共和私人讨论的并发性利益邮件列表的成员(即实际工作在实现这些事情的人):
如果你可以找到不破坏任何线程间发生先前关系的顺序一致的重新排序,它是有效的重新排序(即,符合程序顺序规则和因果关系要求)。
这是John Vint在他的回答中提供的。
strong>
下面的代码(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;
}
}
但是,几个页面后,他们的状态:
p>
- 如果
Resource
是不可变的,任何线程观察/ code>变量将看到它为空或完全构造(由于Java内存模型提供的最终字段的强大保证)
- 但是,没有什么可以阻止指令重新排序:特别是
resource
的两个读取可以被重新排序(在中有一个读取,如果
而在return
)。因此,线程可以在if
条件中看到非空资源
,但返回空引用(* li>
我认为 UnsafeLazyInitialization.getInstance()
code> Resource 是不可变的。
注意:我需要一个参数回答而不是纯yes或no语句。
(*)以更好地了解我对重新排序的观点,此博客文章由杰里米·曼森,谁是并发的JLS的第17章的作者之一,解释String的哈希码是如何安全通过良性数据竞争发布,以及如何去除局部变量的使用可能导致hashcode不正确地返回0,因为可能的重新排序非常类似于上面描述的:
混乱我认为你在这里是作者的意思是安全出版。他指的是非空资源的安全发布,但你似乎得到了。
你的问题很有趣 - 是否可能返回一个空的缓存值的资源?
是的。
编译器允许重新排序操作,例如
public static Resource getInstance(){
Resource reordered = resource;
if(resource!= null){
return reordered;
}
return(resource = new Resource());
}
这不违反顺序一致性规则,但可以返回一个空值。
无论这是否是最好的实施方案,都是争论的问题,但是没有规则阻止这种类型的重新排序。
Comment on accepted answer
This question has generated much more heat than I woul dhave imagined. An important conclusion I drew from public and private discussions with members of the concurrency interest mailing list (i.e. people who actually work on implementing those things):
If you can find a sequentially consistent reordering that does not break any inter-thread happens-before relationship, it is a valid reordering (i.e. is compliant with the program order rule and causality requirement).
That's what John Vint has provided in his answer.
Original question
The code below (Java Concurrency in Practice listing 16.3) is not thread safe for obvious reasons:
public class UnsafeLazyInitialization {
private static Resource resource;
public static Resource getInstance() {
if (resource == null)
resource = new Resource(); // unsafe publication
return resource;
}
}
However, a few pages later, in section 16.3, they state:
I don't understand that statement:
- if
Resource
is immutable, any thread observing theresource
variable will either see it null or fully constructed (thanks to the strong guarantees of final fields provided by the Java Memory Model) - however, nothing prevents instruction reordering: in particular the two reads of
resource
could be reordered (there is one read in theif
and one in thereturn
). So a thread could see a non nullresource
in theif
condition but return a null reference (*).
I think UnsafeLazyInitialization.getInstance()
can return null even if Resource
is immutable. Is it the case and why (or why not)?
Note: I expect an argumented answer rather than pure yes or no statements.
(*) to better understand my point about reordering, this blog post by Jeremy Manson, who is one of the authors of the Chapter 17 of the JLS on concurrency, explains how String's hashcode is safely published via a benign data race and how removing the use of a local variable can lead to hashcode incorrectly returning 0, due to a possible reordering very similar to what I describe above:
The confusion I think you have here is what the author meant by safe publication. He was referring to the safe publication of a non-null Resource, but you seem to get that.
Your question is interesting - is it possible to return a null cached value of resource?
Yes.
The compiler is allowed to reorder the operation like such
public static Resource getInstance(){
Resource reordered = resource;
if(resource != null){
return reordered;
}
return (resource = new Resource());
}
This doesn't violate the rule of sequential consistency but can return a null value.
Whether or not this is the best implementation is up for debate but there is no rules to prevent this type of reordering.
这篇关于不变性和重新排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!