在约http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html底部,它说:为什么在双重检查锁定中不可变对象是安全的?
双检锁不可变对象
如果Helper是不可变对象,使得所有助手的字段是决赛,然后双击 - 检查锁定将无需使用易失性字段。这个想法是,对一个不可变对象(比如一个String或一个Integer)的引用应该和int或者float类似。读取和写入对不可变对象的引用是原子的。
样品和一个可变的解释如下:
// 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...
}
第一个原因,它不工作
最明显的原因,它不工作它,初始化助手对象和写入助手字段的写入操作可以完成或不按顺序感知。因此,调用getHelper()的线程可以看到对助手对象的非空引用,但请参阅助手对象的字段的默认值,而不是在构造函数中设置的值。
如果编译器内联调用构造函数,那么如果编译器可以证明构造函数不能抛出异常或执行同步,则初始化对象和写入辅助对象字段的写入操作可以自由重新排序。
即使编译器没有对这些写入重新排序,在多处理器上,处理器或内存系统可能会重新排列这些写入,如在另一个处理器上运行的线程所感知的那样。
我的问题是:为什么不可变的类没有问题?我无法看到重新排序与班级是否可变的任何关系。
谢谢
我认为你的术语“完全初始化”意味着与你的链接中的东西有所不同? “一个对象在其构造函数完成时被认为是完全初始化的,一个线程只能在该对象被完全初始化后才能看到对象的引用,保证能够看到该对象最终字段的正确初始化值。 – 2014-10-29 01:56:55
不,它是相同的:在调用'new Helper()'之后,JMM保证final字段已经被初始化,但没有说可能或未被初始化的常规字段。 – assylias 2014-10-29 07:03:23