Java最终字段的值的’最新’保证是否延伸到间接引用?

Java语言规范定义了第17.5节中最终字段的语义:

最终字段的使用模型很简单。 在该对象的构造函数中设置对象的最终字段。 在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用。 如果遵循此操作,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。 它还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的。

我的问题是 – ‘最新’保证是否扩展到嵌套数组和嵌套对象的内容?

简而言之:如果一个线程将可变对象图分配给对象中的最终字段,并且对象图永远不会更新,那么所有线程都可以通过最终字段安全地读取该对象图吗?

示例场景:

  1. 线程A构造一个ArrayLists的HashMap,然后在类’MyClass’的实例中将HashMap分配给final字段’myFinal’
  2. 线程B看到对MyClass实例的(非同步)引用并读取’myFinal’,并访问和读取其中一个ArrayLists的内容

在这种情况下,线程B看到的ArrayList的成员是否保证至少与MyClass的构造函数完成时一样最新?

我正在寻找澄清Java内存模型和语言规范的语义,而不是像同步这样的替代解决方案。 我的梦想答案是肯定或否定,并参考相关文字。

更新:

  • 我对Java 1.5及更高版本的语义感兴趣,即通过JSR 133引入更新的Java内存模型。此更新中引入了最终字段的“最新”保证。

在这种情况下,线程B看到的ArrayList的成员是否保证至少与MyClass的构造函数完成时一样最新?

对,他们是。

第一次遇到引用时,需要一个线程来读取内存。 因为构造了哈希映射,所以其中的所有条目都是全新的,然后对对象的引用是up-to-date的构造函数完成时的引用。

在初次遭遇之后,通常的可见性规则适用。 因此,当其他线程更改最终引用中的非final字段时,另一个线程可能看不到该更改,但仍会看到构造函数中的引用。

实际上,这意味着如果你不在构造函数之后修改final hash-map,它的内容就是所有线程的常量。

编辑

我知道我以前见过这个保证。

以下是本文中描述JSR 133的一段感兴趣的内容

初始化安全性

新的JMM还试图提供初始化安全性的新保证 – 只要对象被正确构造(意味着在构造函数完成之前未发布对对象的引用),那么所有线程都将看到它的最终字段是在其构造函数中设置的,无论是否使用同步将引用从一个线程传递到另一个线程。 此外,通过正确构造的对象的最终字段可以到达的任何变量,例如最终字段引用的对象的字段,也保证对其他线程也是可见的。 这意味着如果final字段包含对LinkedList的引用,除了对其他线程可见的引用的正确值之外,在构造时该LinkedList的内容对于没有同步的其他线程也是可见的。 结果是最终意义的显着增强 – 最终字段可以在没有同步的情况下安全地访问,并且编译器可以假设最终字段不会改变,因此可以优化多个提取。

如果构造函数是这样写的,那么你应该没有问题:

 public class MyClass { public final Map myFinal; public MyClass () { Map localMap = new HashMap(); localMap.put("key", new ArrayList()); this.myFinal = localMap; } } 

这是因为地图在分配给公共引用之前已完全初始化。 构造函数完成后,最终的Map将是最新的。