Java 双重检查锁定中的 volatile
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
对象的成员字段中看到默认值。它本质上看到了一个部分构造的对象,但引用不为空。
volatile
修饰符将禁止对变量 _barInstance
的任何先前读取或写入操作进行写入或读取。因此,它将确保线程 2 不会看到部分构造的对象。
更多详情:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
据我所知,这是 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
对象的成员字段中看到默认值。它本质上看到了一个部分构造的对象,但引用不为空。
volatile
修饰符将禁止对变量 _barInstance
的任何先前读取或写入操作进行写入或读取。因此,它将确保线程 2 不会看到部分构造的对象。
更多详情:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html