ver*_*tas 5 java multithreading volatile java-ee
下面的代码片段来自Effective Java 2nd Edition Double Checked Locking
//仔细检查实例字段的延迟初始化的惯用语
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized(this) {
result = field;
if (result == null)// Second check (with locking)
field = result = computeFieldValue();
}
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
据我所知,双重检查锁定的主要问题是在第二次检查锁定内重新排序,以便另一个线程可能看到字段/结果的值设置为可能仍然在执行中.为避免这种情况,我们将字段的引用视为易失性,以保证可见性和重新排序.
但这也可以通过以下代码实现
private FieldType field; // non volatile
private volatile boolean fence = false;
FieldType getField() {
if (field == null) { // First check (no locking) // no volatile read
synchronized(this) { // inside synch block no problem of visibilty will latest //value of field
if (field == null) {// Second check (with locking)
Object obj = computeFieldValue();
fence = true; // any volatile write will take. this will make sure statements are //not reorder with setting field as non null.
field = (FieldType)obj; // this will be only set after computeFieldValue has been //completed fully
}
}
}
return field;
}
Run Code Online (Sandbox Code Playgroud)
因此,在完成初始化之后,没有线程将不得不进行易失性读取或同步开销.请看我的假设是否正确?
该JLS(第17.4.5)规定:
" 在对该字段的每次后续读取之前,都会发生对易失性字段(第8.3.1.4节)的写入."
你是不是读的fence变量被更新后,所以线程的更新之间没有"之前发生"关系fence和任何第二个线程.这意味着第二个线程无法保证看到field第一个线程对变量所做的更新.
简而言之,您的"改进"代码是双重检查锁定的破坏实现.
在纯 JMM 中没有办法实现“廉价”的双重检查锁定;必须付出一些东西。
您的解决方案不起作用,因为可以通过以下正常操作重新排序易失性写入。请参阅jsr133 食谱,了解所谓的“roach motel”模型中允许的重新排序。“Roach motel”是比 JMM 更强的模型,因此如果您的解决方案在 roach motel 中失败,那么在 JMM 中也会失败。
roach motel model
reordering between a normal action and a volatile/monitor action
-- <--
| |
| VolatileLoad / MonitorEnter | forbidden
| |
--> allowed --
--> allowed --
| |
| VolatileStore / MonitorExit | forbidden
| |
-- <--
Run Code Online (Sandbox Code Playgroud)
有一种方法可以阻止“蟑螂汽车旅馆”模型中两个正常操作的重新排序
(1) action#1
(2) volatile write
(3) volatile read
(4) action#4
Run Code Online (Sandbox Code Playgroud)
(1) 不能与 (2) 重新排序,(4) 不能与 (3) 重新排序,因此 (1) 和 (4) 不能重新排序。
然而,请注意,“蟑螂汽车旅馆”模型是比 JMM 更强的模型。您无法确定 JVM 是否符合 roach motel 模型。举个具体的例子
action#1
synchronized(new Object()){}
synchronized(new Object()){}
action#4
Run Code Online (Sandbox Code Playgroud)
根据 roach motel 的说法,action#1 和 action#4 不能重新排序;然而,JVM 可以合法地(JMM 允许)删除两个同步块,然后重新排序剩余的两个操作。