5

我正在阅读有关从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;  
}    

它说 usingresult似乎不需要,但实际上确保field在它已经初始化的常见情况下只读取一次。

但我不明白这一点。和直接做有什么区别if(field == null)?我不明白为什么if (result == null)会有所不同,更不用说如上所述了。

4

2 回答 2

5

解释在下一页(我强调):

这个变量的作用是确保该字段在已经初始化的常见情况下只读取一次。虽然不是绝对必要的,但这可以提高性能,并且通过应用于低级并发编程的标准更加优雅。在我的机器上,上面的方法比没有局部变量的明显版本快大约 25%。

作为参考,引用来自 p。第 71 条的 284:在 Effective Java 2nd Edition 中明智地使用延迟初始化。

更新:读取本地变量和易失变量之间的区别在于前者可能会得到更好的优化。易失性变量不能存储在寄存器或缓存中,也不能对它们进行重新排序的内存操作。此外,读取 volatile 变量可能会触发不同线程之间的内存同步。

有关更多详细信息,请参阅Java 并发实践,第 3.1.4 节:可变变量

于 2012-05-09T09:01:20.517 回答
4

该示例中的想法是,我猜结果/字段将被多次使用。访问result更便宜(它不是易变的)。

否则,您在返回时会进行第二次易失性读取。

如果您需要这样做,请改用按需初始化持有者模式。 http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

在对答案本身的评论中添加我的一些澄清......为了清楚起见:

简短版本:局部变量只能位于 cpu(一个)中的寄存器中(如果有多个,则位于 cpu 的核心之一中)。这是最快的速度。必须检查 volatile 变量是否有其他内核/缓存/cpus/内存中的更改,但细节可能非常特定于硬件(缓存行、内存屏障等)。但也是 jvm 特定的,(例如,热点服务器编译器可能会提升非易失性变量)并且它也对重新排序指令施加了限制,以获得可能的性能提升

于 2012-05-09T09:02:47.433 回答