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

下面的代码片段来自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; } 

据我所知,Double Checked Locking的主要问题是在第二次检查锁定中重新排序,以便另一个线程可能会看到字段/结果的值为set,这可能仍然在执行中。 为了避免这种情况,我们将字段的引用视为易失性,以保证可见性和重新排序。

但这也可以通过以下代码实现

 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; } 

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

在纯JMM中无法实现“便宜”的双重锁定锁定; 有些东西要给。

您的解决方案不起作用,因为可以使用以下正常操作重新排序易失性写入。 请参阅jsr133 cookbook,了解允许在所谓的“蟑螂汽车旅馆”模型中进行重新排序。 “罗奇汽车旅馆”是一个比JMM更强大的模型,所以如果你的解决方案在蟑螂汽车旅馆失败,它在JMM失败了。

 roach motel model reordering between a normal action and a volatile/monitor action -- <-- | | | VolatileLoad / MonitorEnter | forbidden | | --> allowed -- --> allowed -- | | | VolatileStore / MonitorExit | forbidden | | -- <-- 

有一种方法可以阻止“蟑螂汽车旅馆”模型中两种正常行为的重新排序

 (1) action#1 (2) volatile write (3) volatile read (4) action#4 

(1)不能与(2)重新排序,而(4)不能与(3)重新排序,因此(1)和(4)不能重新排序。

但是,请注意“蟑螂汽车旅馆”模型比JMM更强大。 您无法确定JVM是否符合蟑螂汽车旅馆模型。 举个具体的例子

 action#1 synchronized(new Object()){} synchronized(new Object()){} action#4 

根据蟑螂汽车旅馆,行动#1和行动#4不能重新排序; 但JVM可以合法地(由JMM允许)删除两个同步块,然后重新排序其余两个动作。

JLS(第17.4.5节)规定:

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

更新后,您不会读取 fence变量,因此更新fence的线程与任何第二个线程之间没有“before-before”关系。 这意味着第二个线程不能保证看到第一个线程对field变量的更新。

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

根据我相信的消息来源,它似乎是一个有效且安全的代码。 写入volatile变量必须强制写入所有其他变量,并且既不能使用volatile也不能使用正常赋值进行重新排序。