1

向其他线程发布值的标准做法是将构造对象分配给volatile字段:作为双向内存围栏的副作用,通过此类字段读取对象的线程保证不会看到部分构造的对象. 但是,如果一个类中的所有字段都是final,那么对其构造函数的调用将自动与任何客户端代码处于发生前的关系,而不需要volatile在引用上使用关键字。

  1. 后者是否比前者有性能优势(从读者的角度来看)?
  2. 这是否意味着 final 字段存在性能成本(例如,它实际上等效于 volatile 访问,不仅在语义上,而且在执行上)?
4

2 回答 2

2

Aleksey Shipilёv有一篇关于此的文章。

2014 年,针对他的具体硬件、OS 和 Java 版本,他得出了这样的结论:

这些习语的性能成本通常淹没在所有其他成本中。在上面的单例示例中,虽然成本是可衡量的,但分配成本或额外内存取消引用的成本可能会大大抵消安全初始化/发布模式本身的成本。

于 2021-08-29T22:27:37.450 回答
1

简短的回答是,它值得在您的 JVM、CPU 上进行基准测试。和选择的操作系统,因为答案将取决于这些因素以及代码中这些操作的频率(以及因此 JIT 决定如何处理它们)。

但是,我会直观地说,对于 final 字段,编译器对如何使用该字段有更多的确定性,因此它可以使用简单、有效的优化。它可以在具有最终字段的构造函数之后放置一个内存屏障以满足内存保证。

对于volatile字段,编译器需要做更多的工作来确定什么是安全的,并且可能需要避免执行其他优化,例如重新排序读/写。

于 2021-09-04T11:09:22.983 回答