做“最新”的事情Java 最终字段值的保证扩展到间接引用?

发布于 2024-09-01 03:05:22 字数 898 浏览 8 评论 0原文

Java 语言规范在 第 17.5 节中定义了最终字段的语义< /a>:

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

我的问题是 - “最新”保证是否扩展到嵌套数组和嵌套对象的内容?

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

示例场景:

  1. 线程 A 构造 ArrayList 的 HashMap,然后将 HashMap 分配给类“MyClass”实例中的最终字段“myFinal”。
  2. 线程 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:

  1. Thread A constructs a HashMap of ArrayLists, then assigns the HashMap to final field 'myFinal' in an instance of class 'MyClass'
  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

卷耳 2024-09-08 03:05:22

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

是的,他们是。

当线程第一次遇到引用时,需要读取内存。由于哈希映射已构建,其中的所有条目都是全新的,因此对对象的引用是构造函数完成时的最新内容。

初次相遇后,通常的可见性规则将适用。因此,当其他线程更改最终引用中的非最终字段时,其他线程可能看不到该更改,但它仍然会看到来自构造函数的引用。

实际上,这意味着如果您不在构造函数之后修改最终的哈希映射,则其内容对于所有线程都是常量。

编辑

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

以下是这篇文章中描述 JSR 133 的一段有趣内容

初始化安全

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

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?

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

Initialization safety

The new JMM also seeks to provide a
new guarantee of initialization safety
-- that as long as an object is properly constructed (meaning that a
reference to the object is not
published before the constructor has
completed), then all threads will see
the values for its final fields that
were set in its constructor,
regardless of whether or not
synchronization is used to pass the
reference from one thread to another.
Further, any variables that can be
reached through a final field of a
properly constructed object, such as
fields of an object referenced by a
final field, are also guaranteed to be
visible to other threads as well. This
means that if a final field contains a
reference to, say, a LinkedList, in
addition to the correct value of the
reference being visible to other
threads, also the contents of that
LinkedList at construction time would
be visible to other threads without
synchronization. The result is a
significant strengthening of the
meaning of final -- that final fields
can be safely accessed without
synchronization, and that compilers
can assume that final fields will not
change and can therefore optimize away
multiple fetches.

孤凫 2024-09-08 03:05:22

如果构造函数是这样编写的,则应该没有问题:

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

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

If the constructor is written like this, you should have no issue:

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文