最终字段如何阻止其他线程看到部分构造的对象?

时间:2013-08-08 01:09:41

标签: java concurrency

我正在研究创建一个具有final字段的不可变数据类型(包括在分配给最终成员字段之前构造和填充的数组),并注意到似乎指定了JVM以保证其他任何字段获取对此对象的引用的线程将看到初始化的字段和数组值(假设在构造函数中没有发布指向this的指针,请参阅What is an "incompletely constructed object"?How do JVM's implicit memory barriers behave when chaining constructors?)。

我很好奇如何在不同步每个访问此对象的情况下实现此目标,或者以其他方式支付一些重要的性能损失。根据我的理解,JVM可以通过执行以下操作来实现此目的:

  1. 在构造函数的末尾发出写入栅栏
  2. 仅在写入栅栏
  3. 之后发布对新对象的引用
  4. 每次引用对象的最终字段时都会发出读取围栏
  5. 我想不出更简单或更便宜的方法来消除其他线程看到未初始化的最终字段(或通过最终字段的递归引用)的风险。

    由于读取对象的其他线程中的所有读取围栏,这似乎会造成严重的性能损失,但是消除读取围栏会引发在发出对象引用之前在另一个处理器中看到对象引用的可能性读取栅栏或以其他方式查看对应于新初始化的最终字段的存储器位置的更新。

    有谁知道这是如何工作的?这是否会带来显着的性能损失?

1 个答案:

答案 0 :(得分:4)

请参阅this writeup中的“内存障碍”部分。

在设置最终字段之后以及将对象引用分配给另一个变量之前,需要 StoreStore 屏障。这是您要问的关键信息。

根据那里的“重新排序”部分,无法对包含最终字段的对象的引用存储重新排序最终字段的存储。

此外,它指出在v.afield = 1; x.finalField = v; ... ; sharedRef = x;中,前两个都不能与第三个重新排序;这确保了存储为最终字段的对象字段的存储本身保证在存储对包含最终字段的对象的引用之前对其他线程可见。

这意味着,在存储对包含该字段的对象的引用之前,所有线程的所有存储必须对所有线程可见。