做“最新”的事情Java 最终字段值的保证扩展到间接引用?
Java 语言规范在 第 17.5 节中定义了最终字段的语义< /a>:
final 字段的使用模型很简单。在对象的构造函数中设置该对象的最终字段。在对象的构造函数完成之前,不要将对正在构造的对象的引用写入其他线程可以看到的位置。如果遵循这一点,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。它还会看到这些最终字段引用的任何对象或数组的版本,这些版本至少与最终字段一样最新。
我的问题是 - “最新”保证是否扩展到嵌套数组和嵌套对象的内容?
简而言之:如果一个线程将可变对象图分配给对象中的最终字段,并且该对象图从未更新,那么所有线程是否可以通过最终字段安全地读取该对象图?
示例场景:
- 线程 A 构造 ArrayList 的 HashMap,然后将 HashMap 分配给类“MyClass”实例中的最终字段“myFinal”。
- 线程 B 看到对 MyClass 实例的(非同步)引用并读取“myFinal”,并访问和读取 ArrayList 之一的内容
在这种情况下,线程 B 所看到的 ArrayList 成员是否保证至少与 MyClass 的构造函数完成时一样最新?
我正在寻找 Java 内存模型和语言规范的语义澄清,而不是同步等替代解决方案。我梦想的答案是肯定或否定,并参考相关文本。
更新:
- 我对 Java 1.5 及更高版本的语义感兴趣,即通过 JSR 133 引入的更新的 Java 内存模型。此更新中引入了对 Final 字段的“最新”保证。
The Java language spec defines semantics of final fields in section 17.5:
The usage model for final fields is a simple one. Set the final fields for an object in that object's constructor. Do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.
My question is - does the 'up-to-date' guarantee extend to the contents of nested arrays, and nested objects?
In a nutshell: If one thread assigns a mutable object graph to a final field in an object, and the object graph is never updated, can all threads safely read that object graph via the final field?
An example scenario:
- Thread A constructs a HashMap of ArrayLists, then assigns the HashMap to final field 'myFinal' in an instance of class 'MyClass'
- Thread B sees a (non-synchronized) reference to the MyClass instance and reads 'myFinal', and accesses and reads the contents of one of the ArrayLists
In this scenario, are the members of the ArrayList as seen by Thread B guaranteed to be at least as up to date as they were when MyClass's constructor completed?
I'm looking for clarification of the semantics of the Java Memory Model and language spec, rather than alternative solutions like synchronization. My dream answer would be a yes or no, with a reference to the relevant text.
Updates:
- I'm interested in the semantics of Java 1.5 and above, i.e. with the updated Java Memory Model introduced via JSR 133. The 'up-to-date' guarantee on final fields was introduced in this update.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的,他们是。
当线程第一次遇到引用时,需要读取内存。由于哈希映射已构建,其中的所有条目都是全新的,因此对对象的引用是构造函数完成时的最新内容。
初次相遇后,通常的可见性规则将适用。因此,当其他线程更改最终引用中的非最终字段时,其他线程可能看不到该更改,但它仍然会看到来自构造函数的引用。
实际上,这意味着如果您不在构造函数之后修改最终的哈希映射,则其内容对于所有线程都是常量。
编辑
我知道我以前在某处见过这个保证。
以下是这篇文章中描述 JSR 133 的一段有趣内容
Yes, they are.
A thread is required to read memory when it encounters reference for the first time. Because hash map is constructed, all entries in it are brand new, then the references to objects are
up-to-date
to what they were when the constructor has finished.After that initial encounter, the usual visibility rules apply. So, when other thread changes non-final field in the final references, the other thread may not see that change, but it still will see the reference that came out of constructor.
In reality, it means that if you do not modify final hash-map after the constructor, its contents are constants for all threads.
EDIT
I knew that I've seen this guarantee somewhere before.
Here is a paragraph of interest from this article that describes JSR 133
如果构造函数是这样编写的,则应该没有问题:
这是因为映射在分配给公共引用之前已完全初始化。一旦构造函数完成,最终的 Map 将是最新的。
If the constructor is written like this, you should have no issue:
This is because the map is fully initialized before it's assigned to the public reference. Once the constructor completes, the final Map will be up-to-date.