Java双重检查锁定-字符串 [英] Java double checked locking - Strings
问题描述
鉴于string
包含final字段,这是否意味着在双重检查锁定的上下文中没有必要将它们声明为volatile
?例如
class SomeClass{
private String val;
String getVal(){
if(val == null){
synchronized(this){
if(val ==null)
val = new String("foo");
}
}
}
}
我以一个字符串为例,但是它应该与声明某些最终字段的其他对象一起使用,对吗?
不,您仍然必须在这里将val
声明为volatile.问题是,尽管String
是不可变的并且是线程安全的,但val
却不是. val
本身仍然存在可见性问题.
要解决关于假设String包含最终字段"的观点,请注意,JLS特别指出,在处理final
字段时可见性不是可传递的.
给出写操作w,冻结f,操作a(不是对最终字段的读取),对最终字段的读取r1(冻结为f)和读取r2,使得hb(w,f) ,hb(f,a),mc(a,r1)和dereferences(r1,r2),然后在确定r2可以看到哪些值时,我们考虑hb(w,r2). (此事件发生在订购之前不会与其他事件发生在订购之前暂时关闭.)
https://docs. oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5
JLS如何引用final
字段语义的线程安全部分,即实际上使该字段引用的对象可见的部分.
(在某些情况下,您可以通过 synchronizes-with 和 happens-before来依赖传递性. Brian Goetz称其为小猪-支持,并在 Java并发实践中讨论它.但这只是很多专家,在您是Java内存模型的专家之前,我不建议您这样做.)
简而言之,将val
声明为volatile,不用担心通过跳过同步来节省两纳秒的情况.代码中多余的琐事是不值得的,而且还是无法正常工作.
Given that string
s contain final field, does it mean in the context of double checked locking it is not necessary to declare them volatile
? E.g.
class SomeClass{
private String val;
String getVal(){
if(val == null){
synchronized(this){
if(val ==null)
val = new String("foo");
}
}
}
}
I used a string as an example, but it should work with other objects that declare some final field, correct?
No, you still have to declare val
as volatile here. The problem is that while String
is immutable and thread safe, val
is not. You still have a visibility problem with val
itself.
To address your point about "given that String contains a final field," note that the JLS specifically says that visibility is not transitive when dealing with final
fields.
Given a write w, a freeze f, an action a (that is not a read of a final field), a read r1 of the final field frozen by f, and a read r2 such that hb(w, f), hb(f, a), mc(a, r1), and dereferences(r1, r2), then when determining which values can be seen by r2, we consider hb(w, r2). (This happens-before ordering does not transitively close with other happens-before orderings.)
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5
Where a "freeze f" is how the JLS refers to the thread-safe part of final
field semantics, i.e., the part that actually makes the object referenced by the field visible.
(There are cases where you can rely on transitivity with synchronizes-with and happens-before. Brian Goetz calls this 'piggy-backing' and talks about it in Java Concurrency in Practice. But it's pretty much experts only and I don't recommend it until you are an expert with the Java memory model.)
In short, declare val
volatile and don't worry about saving two nanoseconds by skipping synchronization. The extra rigmarole in the code isn't worth it, and it doesn't work anyway.
这篇关于Java双重检查锁定-字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!