【问题标题】:Java immutable classes vs. volatile referencesJava 不可变类与可变引用
【发布时间】:2021-08-29 15:52:35
【问题描述】:
向其他线程发布值的标准做法是将构造对象分配给volatile 字段:作为双向内存栅栏的副作用,通过此类字段读取对象的线程保证不会查看部分构造的对象。
但是,如果一个类中的所有字段都是final,那么对其构造函数的调用会自动与任何客户端代码处于发生前关系,而无需在引用上使用volatile 关键字.
- 与前者相比,后者是否有性能优势(从读者的角度来看)?
- 这是否意味着 final 字段存在性能成本(例如,它实际上等效于 volatile 访问,不仅在语义上,而且在执行上)?
【问题讨论】:
标签:
java
final
java-memory-model
happens-before
【解决方案1】:
Aleksey Shipilёv 的 an article 对此进行了讨论。
2014年,针对他的具体硬件、操作系统和Java版本,他得出了这样的结论:
这些习语的性能成本通常会淹没在所有其他成本中。在上面的单例示例中,虽然成本是可衡量的,但分配成本或额外内存取消引用的成本可能会大大抵消安全初始化/发布模式本身的成本。
【解决方案2】:
简短的回答是,它值得在您的 JVM、CPU 上进行基准测试。和选择的操作系统,因为答案将取决于这些因素以及代码中这些操作的频率(以及 JIT 决定如何处理它们)。
但是我要直观地说,对于 final 字段,编译器对如何使用该字段有更多的确定性,因此它可以使用简单、有效的优化。它可以在具有最终字段的构造函数之后放置一个内存屏障以满足内存保证。
对于volatile 字段,编译器需要做更多的工作来确定什么是安全的,并且可能需要避免执行其他优化,例如重新排序读取/写入。