为什么这个包含两个易失性写入数据竞争的 Java 程序是免费的?

Why is this Java program containing two volatile writes data race free?

考虑以下 Java 程序:

static volatile int shared;

public static void main(final String[] args) {
  final Runnable r = () -> {
    shared = 1;
  };

  new Thread(r).start();
  new Thread(r).start();
}

因为shared被标记为volatile,我想说这个程序没有数据竞争。但是,如何基于 JLS(例如版本 11)来激发这一点?

第 17 章告诉我们:

When a program contains two conflicting accesses (§17.4.1) that are not ordered by a happens-before relationship, it is said to contain a data race.

我认为这是 JLS 提供的数据竞争的定义。然后17.4.1节告诉我们:

Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write.

好的,所以我们在这里有冲突的访问,因为我们有两次写入 shared。现在我们必须在两次写入之间有一个 happens-before 关系,否则我们就会发生竞争。然而,我一直没能找到为什么我们在这里有这样的关系。第 17 章告诉我:

If an action x synchronizes-with a following action y, then we also have hb(x, y).

而同一章告诉我:

A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).

但我还没有找到任何 happens-before 将写入与 volatile 变量相关联。这是为什么?

访问 volatile 变量从不 受到数据竞争

从JLS看不是很明显,但是在短语

(§17.4.1) Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write.

术语“读取”和“写入”不是通用术语,但在下一节“17.4.2 操作”中描述为对 非易失性 变量的访问:

Read (normal, or non-volatile). Reading a variable.

Write (normal, or non-volatile). Writing a variable.

在那一节中,对 volatile 变量的访问被归类为 同步操作 的一部分,使用不同的术语“易失性读取”和“易失性写入":

Volatile read. A volatile read of a variable.

Volatile write. A volatile write of a variable.

因为只有(非易失性)读取和写入可能会发生冲突,所以只有那些访问可能包含 数据竞争易失性 读取和写入永远不会发生冲突,并且永远不会 包含数据竞争