Java双重检查锁定-字符串 [英] Java double checked locking - Strings

查看:103
本文介绍了Java双重检查锁定-字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

鉴于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 strings 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆