java volatile 的语义是否保证不会出现错误的结果? (两个线程先写后读)

Does the semantics of java volatile guarantee that wrong results will not appear? (Two threads write first and then read)

volatile x=y=0

Thread1
x=1
r1=y

Thread2
y=1
r2=x

r1 和 r2 是局部变量

问题一:

r1==r2==0的结果是非法的,没有出现吗?

所有语句都是写入或读取volatile字段,按常理应该是r1==1r2==1r1==r2==1三种情况之一

让我困惑的是,volatile的语义是当read看到write的结果时,happens-before规则成立。在这个测试中,如果两个线程看不到对方的结果,并不违反happens-before 的规则(更新:这是错误的).

JLS 说 happens-before rules is not enough, And there is a Causality Test Cases。我把它理解为一些补充的特殊情况规则,但是我在那些例外规则中没有找到这个问题的情况。

OOTA 问题需要因果关系。

想象一下下面的代码:

int a=0,b=0 (non volatile!)

thread1:
    r1=a
    b=r1

thread2:
    r2=b
    b=42

r1==r2==42 是否允许执行?是的,因为 x,y 不是易变的,读取可以看到他们正在竞争的值。这符合happens-before的定义一致:

  • 读取需要按照 happens-before 顺序查看最近的写入
  • 读取需要看到它在 data-race 中的写入。

如果您有数据竞争,您可能会得到顺序不一致的执行。

如果我们稍微修改示例:

int a=0,b=0 (non volatile!)

thread1:
    r1=a (1)
    b=r1 (2)

thread2:
    r2=b (3)
    b=r2 (4)

我们能得到'r1==r2==42'吗?假设我们有一些神奇的 CPU 推测 b=42 在 (3):

所以我们得到:

speculate b=42
r2=42 (3)
b=42 (4)
r1=42 (1)
b=42 (2)

CPU 的推测得到了回报,因为 b=42 的推测是正确的!

值42凭空出现,被JMM禁止。为了解决这个问题,每次执行都需要有一些因果顺序,以防止因果循环。

关于问题 1:

Is the result of r1==r2==0 illegal and does not appear?

是的,这是非法的。由于每个变量都是易变的,因此不存在数据竞争,因此只允许顺序一致的执行。

您的问题是您没有看到程序顺序需要在连续一致的执行中得到保留。

volatile x=y=0

Thread1
x=1    (1)
r1=y   (2)

Thread2
y=1    (3)
r2=x   (4)

(1) 由于程序顺序发生在 (2) 之前。

(3) 由于程序顺序发生在 (4) 之前。

在 JMM 中,happens-before 关系被定义为程序顺序和 synchronizes-with 顺序并集的传递闭包。