Java语言规范在17.5节中定义了final字段的语义:
最终字段的使用模型很简单。在该对象的构造函数中设置该对象的最终字段。不要在对象的构造函数完成之前,在另一个线程可以看到它的地方编写对正在构造的对象的引用。如果执行此操作,则当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。它还将查看那些最终字段引用的任何对象或数组的版本,这些版本至少与最终字段一样。
我的问题是-“最新”保证是否扩展到嵌套数组和嵌套对象的内容?
简而言之:如果一个线程将可变对象图分配给对象的最终字段,并且该对象图从未更新,那么所有线程都可以通过最终字段安全地读取该对象图吗?
一个示例场景:
在这种情况下,是否保证线程B看到的ArrayList成员至少与MyClass的构造函数完成时的成员保持最新?
我正在寻找Java内存模型和语言规范的语义的说明,而不是同步之类的替代解决方案。我的梦想答案是是或否,并参考了相关文本。
更新:
对,他们是。
线程在首次遇到引用时需要读取内存。因为哈希映射是构造而成的,所以其中的所有条目都是全新的,因此对对象的引用up-to-date是指构造函数完成后的对象。
up-to-date
初次遇到之后,将应用通常的可见性规则。因此,当其他线程更改最终引用中的非最终字段时,其他线程可能看不到该更改,但仍会看到构造函数发出的引用。
实际上,这意味着如果不在构造函数之后修改最终的哈希映射,则其内容对于所有线程都是常量。
编辑
我知道我以前曾经见过这种保证。
这是本文中描述JSR 133 的一段有趣的内容
初始化安全 新的JMM还寻求提供初始化安全性的新保证- 只要正确构造了一个对象(这意味着在构造函数完成之前不会发布对该对象的引用),那么所有线程都将看到它的最终字段是在其构造函数中设置的,无论是否使用同步将引用从一个线程传递到另一个线程。此外,也可以保证通过适当构造的对象的最终字段(例如,由最终字段引用的对象的字段)可以到达的任何变量也对其他线程可见。这意味着,除了其他线程可以看到的正确值之外,如果最终字段还包含对LinkedList的引用,而且在构建时该LinkedList的内容对于其他线程也是可见的,而无需同步。结果显着增强了final的含义- 可以安全地访问final字段而无需同步,并且编译器可以假定final字段不会更改,因此可以优化多次提取。
初始化安全
新的JMM还寻求提供初始化安全性的新保证- 只要正确构造了一个对象(这意味着在构造函数完成之前不会发布对该对象的引用),那么所有线程都将看到它的最终字段是在其构造函数中设置的,无论是否使用同步将引用从一个线程传递到另一个线程。此外,也可以保证通过适当构造的对象的最终字段(例如,由最终字段引用的对象的字段)可以到达的任何变量也对其他线程可见。这意味着,除了其他线程可以看到的正确值之外,如果最终字段还包含对LinkedList的引用,而且在构建时该LinkedList的内容对于其他线程也是可见的,而无需同步。结果显着增强了final的含义- 可以安全地访问final字段而无需同步,并且编译器可以假定final字段不会更改,因此可以优化多次提取。