Chr*_*ght 15 java concurrency volatile
我正在努力完成有效Java(第二版)的第71项"明智地使用懒惰初始化".它建议使用双重检查习惯用于使用此代码(pg 283)实例字段的延迟初始化:
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;
}
所以,我实际上有几个问题:
为什么field在同步块中进行初始化时需要volatile修饰符?本书提供了这样的支持文本:"因为如果字段已经初始化,则没有锁定,因此将字段声明为volatile 是至关重要的".因此,是否一旦字段初始化,volatile是多线程一致视图的唯一保证,field因为缺少其他同步?如果是这样,为什么不同步getField()或上述代码提供更好的性能?
该文本表明,不需要的局部变量result用于"确保field在已经初始化的常见情况下只读取一次",从而提高性能.如果result被删除,field在已经初始化的常见情况下将如何多次读取?
Gra*_*ray 16
为什么在字段上需要volatile修饰符,因为初始化发生在同步块中?
这volatile是必要的,因为围绕对象构造的指令可能重新排序.Java内存模型声明实时编译器可以选择重新排序指令以在对象构造函数之外移动字段初始化.
这意味着thread-1可以初始化a的field内部,synchronized但是线程2可能会看到未完全初始化的对象.在将对象分配给之前,不必初始化任何非最终字段field.该volatile关键字可确保field被访问之前,它作为完全初始化.
这是着名的"双重检查锁定"错误的一个例子.
如果删除了结果,那么在已经初始化的常见情况下,如何多次读取字段?
无论何时访问某个volatile字段,都会导致跨越内存屏障.与访问普通字段相比,这可能是昂贵的.将volatile字段复制到局部变量是一种常见的模式,如果要在同一方法中以任何方式多次访问它.
有关线程之间没有内存屏障的共享对象的危险的更多示例,请参阅我的答案:
| 归档时间: | 
 | 
| 查看次数: | 1438 次 | 
| 最近记录: |