【发布时间】:2013-06-18 12:41:54
【问题描述】:
以下代码 sn-p 来自 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;
}
据我所知,双重检查锁定的主要问题是第二次检查锁定内部的重新排序,以便其他线程可能会看到已设置的字段/结果的值,而这些值实际上可能仍在执行中。为了避免这种情况,我们将字段的引用设置为 volatile 以保证可见性和重新排序。
但这也可以通过下面的代码来实现
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;
}
因此,在完成初始化之后,就没有线程需要进行易失性读取或同步开销。请 看看我的假设是否正确?
【问题讨论】:
-
现在双重检查锁定不被认为是一件坏事吗? en.wikipedia.org/wiki/Double-checked_locking
-
有一种明确的方法可以避免这种情况:重构您的代码,以便不需要双重检查锁定!在 (100 - epsilon)% 的情况下,这是可行的,而且无论如何您都应该这样做。
标签: java multithreading jakarta-ee volatile