这是Double Check Locking的更好版本,没有易失性和同步开销

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)

因此,在完成初始化之后,没有线程将不得不进行易失性读取或同步开销.请看我的假设是否正确?

Ste*_*n C 9

JLS(第17.4.5)规定:

" 在对该字段的每次后续读取之前,都会发生对易失性字段(第8.3.1.4节)的写入."

你是不是fence变量被更新后,所以线程的更新之间没有"之前发生"关系fence和任何第二个线程.这意味着第二个线程无法保证看到field第一个线程对变量所做的更新.

简而言之,您的"改进"代码是双重检查锁定的破坏实现.


Zho*_*gYu 3

在纯 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 允许)删除两个同步块,然后重新排序剩余的两个操作。