我正在阅读Effective Java
的双重检查锁定。代码执行以下操作:
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;
}
它表示使用result
似乎不需要,但实际上确保field
仅在已初始化的常见情况下只读取一次。
但我不明白这一点。直接做if(field == null)
有什么区别?我不明白为什么if (result == null)
是不同的,更不用说如上所述了。
答案 0 :(得分:5)
解释见下页(我强调):
此变量的作用是确保该字段为 在已经初始化的常见情况下只读一次。虽然没有 严格必要,这可以提高性能,并且标准更优雅 适用于低级并发编程。在我的机器上,方法 上面比没有局部变量的明显版本快25%左右。
供参考,报价来自p。第71项中的284:在Effective Java 2nd Edition中明智地使用延迟初始化。
更新:读取本地变量与变量之间的区别在于前者可以更好地进行优化。易失性变量不能存储在寄存器或高速缓存中,也不能对它们的存储器操作进行重新排序。此外,读取volatile变量可能会触发不同线程之间的内存同步。
有关详细信息,请参阅实践中的Java并发,第3.1.4节:易失性变量。
答案 1 :(得分:4)
在这个例子中的想法是结果/字段将被进一步使用,我想。访问result
更便宜(它不易变)。
否则,在返回时会有第二次易失性读取。
如果需要,请使用initializaton on demand holder模式。 http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom
在评论中添加我的一些澄清答案本身......明确:
短版本:本地变量只能位于(一个)cpu中的寄存器中(如果是多个等,则位于其中一个cpu的内核中)。这和它一样快。必须检查volatile变量是否有其他core / caches / cpus / memory的更改,但细节可能非常特定于硬件(缓存行,内存屏障等)。但是jvm也是特定的(例如,热点服务器编译器可能会提升非易失性变量)并且它对重新排序指令施加限制以获得可能的性能增益