此问题涉及旧Java版本的行为和双重检查锁定算法的旧实现
较新的实现使用volatile并依赖于稍微改变的volatile语义,因此它们不会被破坏.
据说,除了long或double字段外,字段赋值总是原子的.
但是,当我读到为什么双重检查锁定被破坏的解释时,它说问题在于赋值操作:
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other functions and members...
}
Run Code Online (Sandbox Code Playgroud)
- 线程A注意到该值未初始化,因此它获得锁定并开始初始化该值.
- 由于某些编程语言的语义,允许编译器生成的代码在A完成初始化之前更新共享变量以指向部分构造的对象.
- 线程B注意到共享变量已初始化(或显示),并返回其值.因为线程B认为该值已经初始化,所以它不会获得锁定.如果B在B看到A完成的所有初始化之前使用该对象(因为A尚未完成初始化或者因为对象中的某些初始化值尚未渗透到内存B使用(缓存一致性)) ,该程序可能会崩溃.
(来自http://en.wikipedia.org/wiki/Double-checked_locking).
什么时候可能?是否有可能在64位JVM分配操作中不是原子的?如果不是那么"双重检查锁定"是否真的被打破了?
嗨,下面是Effective Java 2nd Edition的片段.在这里,作者声称下面的代码比你不使用结果变量快25%.根据书中的"这个变量的作用是确保该字段在已经初始化的常见情况下只读取一次." .我无法理解为什么这个代码在初始化之后会比较快,如果我们不使用Local变量结果.在任何一种情况下,无论是否使用局部变量结果,初始化后只有一个易失性读取.
// Double-check idiom for lazy initialization of instance fields
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)