Java中的双重检查锁定易失 [英] volatile in double-checked locking in Java
问题描述
据我了解,这是Java(自Java 5起)中双重检查锁定模式的正确实现:
class Foo {
private volatile Bar _barInstance;
public Bar getBar() {
if (_barInstance == null) {
synchronized(this) { // or synchronized(someLock)
if (_barInstance == null) {
Bar newInstance = new Bar();
// possible additional initialization
_barInstance = newInstance;
}
}
}
return _barInstance;
}
}
我想知道是否缺少volatile
是一个严重的错误,还是只是轻微的瑕疵,假设_barInstance
仅通过getBar
访问.
我的想法如下:synchronized
引入了 happens-before 关系.初始化_barInstance
的线程将其值写入主存储器,而离开同步块.因此即使_barInstance
不是volatile
,也不会进行双重初始化:其他线程在其_barInstance
的本地副本中具有null
(在第一次检查中获取true
),但必须读取进入同步块后,在第二次检查中从主存储器中获取新值(获取false
并且不重新初始化).因此,唯一的问题是过多的每线程一个锁获取.
据我了解,这在CLR中是正确的,我相信在JVM中也是正确的.我说的对吗?
谢谢.
在以下情况下,不使用volatile可能会导致错误:
- 线程1输入
getBar()
并找到_barInstance
为null
- 线程1尝试创建
Bar
对象并将对_barInstance
的引用更新.由于某些编译器优化,这些操作可能会无序进行. - 同时,线程2进入
getBar()
并看到非空的_barInstance
,但可能会在_barInstance
对象的成员字段中看到默认值.它实质上看到的是部分构造的对象,但引用不为null.
volatile
修饰符将禁止对任何先前的读取或写入操作写入或读取变量_barInstance
.因此,可以确保线程2不会看到部分构造的对象.
有关更多详细信息: http://www.cs.umd.edu/~pugh /java/memoryModel/DoubleCheckedLocking.html
As I understand, this is a correct implementation of the double-checked locking pattern in Java (since Java 5):
class Foo {
private volatile Bar _barInstance;
public Bar getBar() {
if (_barInstance == null) {
synchronized(this) { // or synchronized(someLock)
if (_barInstance == null) {
Bar newInstance = new Bar();
// possible additional initialization
_barInstance = newInstance;
}
}
}
return _barInstance;
}
}
I wonder if absence of volatile
is a serious error or just a slight imperfection with possible performance drawback assuming _barInstance
accessed only through getBar
.
My idea is the following: synchronized
introduces happens-before relation. The thread that initializes _barInstance
writes its value to the main memory leaving the synchronized block. So there will be no double initialization of _barInstance
even when it isn't volatile
: other threads have null
in theirs local copies of _barInstance
(get true
in the first check), but have to read the new value from the main memory in the second check after entering the synchronized block (get false
and do no re-initialization). So the only problem is an excessive one-per-thread lock acquisition.
As I understand, it's correct in CLR and I believe it's also correct in JVM. Am I right?
Thank you.
Not using volatile may result in errors in the following case:
- Thread 1 enters
getBar()
and finds_barInstance
to benull
- Thread 1 attempts to create a
Bar
object and update the reference to_barInstance
. Due to certain compiler optimisations, these operations may be done out of order. - Meanwhile, thread 2 enters
getBar()
and sees a non-null_barInstance
but might see default values in member fields of the_barInstance
object. It essentially sees a partially constructed object but the reference is not null.
The volatile
modifier will prohibit a write or read of the variable _barInstance
with respect to any previous read or write. Hence, it will make sure that thread 2 will not see a partially constructed object.
For more details: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
这篇关于Java中的双重检查锁定易失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!