带有两个同步块的 DCL 坏了吗?
DCL with two synchronized block is broken?
我无法理解 A fix that doesn't work 中的以下代码片段。
(我确实阅读了同一页上的解释)。
如果我们有2个同步块,这个DCL版本是怎么坏的?还是不适用postJava5?
// (Still) Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
Helper h;
synchronized(this) {
h = helper;
if (h == null)
synchronized (this) {
h = new Helper();
} // release inner synchronization lock
helper = h;
}
}
return helper;
}
// other functions and members...
}
无法保证将 helper
视为非空的线程将能够看到 new Helper();
进行的所有写入。所以你可以访问单例的损坏版本。您需要在将 helper
视为非 null
的线程中确保它看到 after h = new Helper();
完成。观察对非易失性变量的更改并不能建立这种关系,这就是线程所做的一切。
过于简单化了,Java 的内存可见性模型的工作方式是两个线程各自做一些事情,在两个操作之间建立 "happens before" / "happens after" 关系两个线程。这可以包括同步块内的操作或对 volatile 变量的访问。
但是使用上面的代码,线程可以观察到 helper
不是 null
,然后继续访问 new Helper()
创建的对象。它不必访问 volatile
变量,也不必进入同步块。所以没有什么可以建立所需的 "happens after" 关系来确保它看到 new Helper()
.
所做的任何更改
我无法理解 A fix that doesn't work 中的以下代码片段。 (我确实阅读了同一页上的解释)。
如果我们有2个同步块,这个DCL版本是怎么坏的?还是不适用postJava5?
// (Still) Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
Helper h;
synchronized(this) {
h = helper;
if (h == null)
synchronized (this) {
h = new Helper();
} // release inner synchronization lock
helper = h;
}
}
return helper;
}
// other functions and members...
}
无法保证将 helper
视为非空的线程将能够看到 new Helper();
进行的所有写入。所以你可以访问单例的损坏版本。您需要在将 helper
视为非 null
的线程中确保它看到 after h = new Helper();
完成。观察对非易失性变量的更改并不能建立这种关系,这就是线程所做的一切。
过于简单化了,Java 的内存可见性模型的工作方式是两个线程各自做一些事情,在两个操作之间建立 "happens before" / "happens after" 关系两个线程。这可以包括同步块内的操作或对 volatile 变量的访问。
但是使用上面的代码,线程可以观察到 helper
不是 null
,然后继续访问 new Helper()
创建的对象。它不必访问 volatile
变量,也不必进入同步块。所以没有什么可以建立所需的 "happens after" 关系来确保它看到 new Helper()
.