我正在研究创建一个具有final字段的不可变数据类型(包括在分配给最终成员字段之前构造和填充的数组),并注意到似乎指定了JVM以保证获得的任何其他线程对此对象的引用将看到初始化的字段和数组值(假设没有指针this在构造函数中发布,请参阅什么是"未完成构造的对象"?以及JVM的隐式内存屏障在链接构造函数时如何表现?).
我很好奇如何在不同步每个访问此对象的情况下实现此目的,或者以其他方式支付一些重要的性能损失.根据我的理解,JVM可以通过执行以下操作来实现此目的:
我想不出更简单或更便宜的方法来消除其他线程看到未初始化的最终字段(或通过最终字段的递归引用)的风险.
由于读取对象的其他线程中的所有读取栅栏,这似乎可能会造成严重的性能损失,但是消除读取栅栏会导致在发出读取之前在另一个处理器中看到对象引用的可能性.栅栏或以其他方式看到对应于新初始化的最终字段的存储器位置的更新.
有谁知道这是如何工作的?这是否会带来显着的性能损失?
请参阅本文中的“内存屏障”部分。
在设置 Final 字段之后和将对象引用分配给另一个变量之前,需要StoreStore屏障。这是您所询问的关键信息。
根据那里的“重新排序”部分,最终字段的存储不能相对于对包含最终字段的对象的引用的存储进行重新排序。
此外,它指出在 中v.afield = 1; x.finalField = v; ... ; sharedRef = x;,前两个都不能相对于第三个重新排序;这确保了在存储对包含最终字段的对象的引用之前,对存储为最终字段的对象的字段的存储本身对其他线程可见。
总之,这意味着在存储对包含该字段的对象的引用之前,对最终字段的所有存储必须对所有线程可见。