1

在Brian Goetz 等人的《 Java Concurrency in Practice 》一书中:

如果您不能确保在另一个线程加载该共享引用之前发布共享引用,那么可以通过写入其字段来重新排序对新对象的引用的写入(从使用该对象的线程的角度来看)。在这种情况下,另一个线程可以看到对象引用的最新值,但该对象的部分或全部状态(部分构造的对象)的值已过期。

这是否意味着:在发布对象的线程中,对新对象的引用的写入不会随着对其字段的写入而重新排序;对其字段的写入发生在引用的写入之前。但是,该发布线程可能会在刷新更新的对象字段之前刷新对主内存的更新引用。因此,使用对象的线程可能会看到对象的非空引用,但会看到对象字段的过时值?从这个意义上说,操作是为消费线程重新排序的。

4

2 回答 2

1

是的。

你的问题的答案就在你引用的段落中,你似乎在你的问题中回应了答案。


但是有一条评论:您说过,“[the] 发布线程可能会在刷新更新的对象字段之前刷新对主内存的更新引用。” 如果您谈论的是 Java 代码,那么最好坚持使用 Java 语言规范 (JLS) 中编写的内容。

JLS 告诉您允许 Java 程序如何运行。它没有提到“主内存”或“缓存”或“刷新”。它只是说,如果没有显式同步,从某个其他线程的角度来看,一个线程以特定顺序对两个或多个变量执行的更新可能似乎以不同的顺序发生。如何或为什么会发生这种情况是“实施细节”。

于 2021-03-21T13:34:28.657 回答
1
  1. 在发布对象的线程中,对新对象的引用的写入不会与对其字段的写入重新排序;对其字段的写入发生在引用的写入之前。

是的。因为在一个线程中,这个过程发生在不允许重新排序的程序顺序中:“如果 x 和 y 是同一线程的操作,并且 x 在程序顺序中位于 y 之前,则 hb(x, y)。” (https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5)。我们可以改写一下:“对新对象的引用的写入不会随着对其字段的写入而重新排序”,这意味着如果您读取对对象的引用,则可以保证您将连续读取其所有字段.

  1. 使用对象的线程可能会看到对象的非空引用,但会看到对象字段的过时值?

是的,当您以不安全的方式发布对象时,可能会出现这种情况,而没有使用内存屏障实现适当的 HB 边缘。从字面上看,在没有 HB/membars 的情况下,您会得到未定义的行为。这意味着在其他线程中,您可以查看/读取任何内容(JMM 明确禁止的空气中的 (OoTA) 值除外)。安全发布使所有观察发布对象的读者都可以看到发布之前写入的所有值。确保出版物安全的最流行和最简单的方法很少:

您还可以使用其他产生 HB 的操作,如 Thread.start() 等,但我的日常最爱是:

  • 不可变数据的最终字段
  • volatile/AtomicXXX 字段和锁(显式同步块/ReadWriteLock,BlockingQueue 中的隐式锁)用于可变数据。
于 2021-03-23T13:53:39.160 回答