Java双重检查锁定 - 字符串

时间:2015-11-12 12:50:05

标签: java multithreading double-checked-locking

鉴于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字段的其他对象一起使用,对吗?

2 个答案:

答案 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易变,并且不用担心通过跳过同步来节省两纳秒。代码中额外的琐事并不值得,而且无论如何它都不起作用。