Java 语言规范定义了 Final 字段的语义第17.5节:
Final 字段的使用模型很简单。在对象的构造函数中设置该对象的最终字段。在对象的构造函数完成之前,请勿将对正在构造的对象的引用写入其他线程可以看到的位置。如果遵循这一点,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。它还将看到这些最终字段引用的任何对象或数组的版本,这些版本至少与最终字段一样最新。
我的问题是 - “最新”保证是否扩展到嵌套数组和嵌套对象的内容?
简而言之:如果一个线程将可变对象图分配给对象中的最终字段,并且该对象图从未更新,那么所有线程是否可以通过最终字段安全地读取该对象图?
一个示例场景:
- 线程 A 构造 ArrayLists 的 HashMap,然后将 HashMap 分配给类“MyClass”实例中的最终字段“myFinal”
- 线程 B 看到对 MyClass 实例的(非同步)引用并读取“myFinal”,然后访问并读取 ArrayList 之一的内容
在这种情况下,线程 B 所看到的 ArrayList 成员是否保证至少与 MyClass 的构造函数完成时一样最新?
我正在寻求 Java 内存模型和语言规范的语义澄清,而不是同步等替代解决方案。我梦想的答案是肯定或否定,并参考相关文本。
更新:
- 我对 Java 1.5 及更高版本的语义感兴趣,即通过 JSR 133 引入的更新的 Java 内存模型。此更新中引入了对 Final 字段的“最新”保证。
在这种情况下,成员是
线程 B 看到的 ArrayList
保证至少达到
日期与 MyClass 时一样
构造函数完成了吗?
是的,他们是。
当线程第一次遇到引用时,需要读取内存。因为构建了哈希映射,其中的所有条目都是全新的,那么对对象的引用就是up-to-date
到构造函数完成时的状态。
初次相遇后,通常的可见性规则将适用。因此,当其他线程更改最终引用中的非最终字段时,其他线程可能看不到该更改,但它仍然会看到来自构造函数的引用。
实际上,这意味着如果您不在构造函数之后修改最终的哈希映射,则其内容对于所有线程都是常量。
EDIT
我知道我以前在某处见过这个保证。
这是一段有趣的内容article描述 JSR 133
初始化安全
新的 JMM 还力求提供
初始化安全的新保证
——只要一个对象被正确构造(意味着一个
对对象的引用不是
在构造函数之前发布
完成),然后所有线程都会看到
其最终字段的值
在其构造函数中设置,
无论是否
同步用于传递
从一个线程到另一个线程的引用。
此外,任何变量都可以
通过最后一个字段达到
正确构造的对象,例如
a 引用的对象的字段
最后的字段,也保证是
对其他线程也可见。这
意味着如果最终字段包含
引用,比如说,一个 LinkedList,
除了正确的值
引用对其他人可见
线程,还有该线程的内容
LinkedList 在构造时会
对其他线程可见,无需
同步。结果是
显着加强
Final 的含义——final 字段
可以安全地访问,无需
同步,并且编译器
可以假设最终字段不会
改变,因此可以优化
多次获取。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)