鉴于string
包含final字段,它是否意味着在双重检查锁定的上下文中,没有必要声明它们volatile
? E.g。
class SomeClass{
private String val;
String getVal(){
if(val == null){
synchronized(this){
if(val ==null)
val = new String("foo");
}
}
}
}
我使用了一个字符串作为例子,但它应该与声明某些final字段的其他对象一起使用,对吗?
答案 0 :(得分:2)
对于字符串你是对的。声明为final的字符串不能有所不同,因此在使用时不需要同步。
其他对象不适用。以这个小班为例:
public class BankAccount {
private int balance = 0;
public void addMoney(int money) {
balance+=money;
}
}
当你有这个类的最终对象时,并不意味着没有人可以改变对象内的字段。你不能将其他东西分配给最终变量!
结论:访问final String时,根据Object本身的不同,在访问最终的Objects时可能不需要同步。
答案 1 :(得分:2)
不,你仍然需要在这里声明val
为volatile。问题是虽然String
是不可变的并且线程安全,但val
不是。{1}}。 val
本身仍然存在可见性问题。
为了解决你的观点,因为String包含一个最终字段,"请注意,JLS明确表示在处理final
字段时可见性不可传递。
给定写入w,冻结f,动作a(不是最终字段的读取),f的最终字段的读取r1和读取r2使得hb(w,f) ,hb(f,a),mc(a,r1)和解除引用(r1,r2),然后在确定r2可以看到哪些值时,我们考虑hb(w,r2)。 (这种情况发生 - 在订购之前不会与其他发生前的订单传递关闭。)
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5
"冻结f"是JLS如何引用final
字段语义的线程安全部分,即实际使字段引用的对象可见的部分。
(有些情况下 依赖于传递性,与同步,之前发生。 Brian Goetz称之为&#39捎带'并在 Java Concurrency in Practice中讨论它。但它只是专家而且我不推荐它,直到你成为专家Java内存模型。)
简而言之,声明val
易变,并且不用担心通过跳过同步来节省两纳秒。代码中额外的琐事并不值得,而且无论如何它都不起作用。