发生在关系之前

(以下是 Java 语言规范所说的简化版本。为了更深入地理解,你需要阅读规范本身。)

发生之前 - 关系是内存模型的一部分,它允许我们理解和推理内存可见性。正如 JLS 所说( JLS 17.4.5 ):

“两个动作可以通过先发生关系来排序。如果一个动作发生在另一个动作之前,那么第一个动作第二个动作之前可见并在第二个之前被命令。”

这是什么意思?

操作

上述引用所引用的操作在 JLS 17.4.2 中指定。规范中列出了 5 种列出的操作:

  • 阅读:读取非易失性变量。

  • 写:写一个非易失性变量。

  • 同步动作:

    • 易失性读取:读取易失性变量。

    • 易失写:写一个 volatile 变量。

    • 锁。锁定显示器

    • 开锁。解锁显示器。

    • 线程的(合成)第一个和最后一个动作。

    • 启动线程或检测线程已终止的操作。

  • 外部行动。具有取决于程序所在环境的结果的操作。

  • 线程分歧行动。这些模拟了某些无限循环的行为。

程序顺序和同步顺序

这两个排序( JLS 17.4.3JLS 17.4.4 )控制 Java 中语句的执行

程序顺序描述了单个线程中语句执行的顺序。

同步顺序描述了由同步连接的两个语句的语句执行顺序:

  • 监视器上的解锁操作该监视器上的所有后续锁定操作同步

  • 对 volatile 变量的写入任何线程对同一变量的所有后续读取同步

  • 启动线程的动作(即对 Thread.start() 的调用) 它启动的线程中的第一个动作同步 (即调用线程的 run() 方法)。

  • 字段的默认初始化每个线程中的第一个操作同步。 (有关此问题的解释,请参阅 JLS。)

  • 线程中的最后一个操作与另一个检测终止的线程中的任何操作同步。例如返回 join() 呼叫或 isTerminated() 呼叫返回 true

  • 如果一个线程中断另一个线程,则第一个线程中的中断调用另一个线程检测到线程被中断的点同步

发生在订单之前

这种排序( JLS 17.4.5 )决定了是否保证存储器写入对后续存储器读取可见。

更具体地说,变量 v 的读取保证观察对 v 的写入,当且仅当 write(v) 发生时 - 在 read(v) 之前并且没有插入到 v 的写入。如果有干预写入,则 read(v) 可能会看到它们的结果而不是之前的结果。

定义事先排序的规则如下:

  • 发生在规则#1 之前 - 如果 x 和 y 是同一个线程的动作,并且 x 在程序顺序中出现在 y 之前,则 x 发生在 y 之前

  • 发生在规则#2 之前 - 从对象的构造函数的末尾到该对象的终结器的开始有一个发生在前的边缘。

  • 发生在规则#3 之前 - 如果动作 x 后续动作 y 同步,则 x 发生在 y 之前

  • 发生在规则#4 之前 - 如果 x 发生 - 在 y 和 y 发生之前 - 在 z 之前然后 x 发生在 z 之前

此外,Java 标准库中的各种类被指定为定义发生在之前的关系。你可以将其解释为意味着它以某种方式发生,而无需确切知道如何满足保证。