在chapter 17 of Java language specification中,有一节解释为什么"发生 - 在一致性不充分之前"。这是一个例子:
At first, x = y = 0
Thread 1 | Thread 2
r1 = x; | r2 = y;
if (r1 != 0) y = 1; | if (r2 != 0) x = 1;
这是一个可能的执行追踪:
r1 = x; // sees write of x = 1
y = 1;
r2 = y; // sees write of y = 1
x = 1;
怎么会发生这种情况?令我困惑的是,当第一个动作看到x = 1时,它不意味着条件r2!= 0变为真,因此y被分配给1?但按顺序,在r1 = x之后,y = 1。我在哪里弄错了理解这个例子?我该如何正确理解这个例子?
答案 0 :(得分:6)
我相信这个例子在Java规范中提出的观点是Hans Boehm等人的观点。在Outlawing Ghosts中写一下,它指出了某些当代语言(Java,C ++ 11,甚至是C ++ 14)的内存模型的不足,但它们已解决但未解决此问题。 / p>
重点在于:编写的程序通过语言规则正确同步。 (如果你在任何地方都使用原子变量和memory_order_relaxed
,那么在C ++中也是如此。)但是,仍然不禁止发生意外行为。用Boehm来解释:机器可以推测的值,例如x
为1,然后执行结果分支,稍后(大概是当内存最终响应时)验证猜测是真的。确实找到了猜测,因为在此期间另一个线程确实存储了x = 1
,机器继续并且不会回滚推测的执行。
更糟糕的是,CPU确实可以推测任何值存在。考虑这个修改过的例子:
r1 = x | r2 = y
if (r1 != 0) y = r1 | if (r2 != 0) x = r2
在这种情况下,出于同样的原因,x
和y
可能会以任何值结束。机器可以推测价值是任何东西,然后用这个假设推测性地继续执行,然后在众所周知的自我实现的预言中发现它的推测是正确的。
或许可以放心,目前没有真正的硬件表现得像这样。但重点是当代语言的记忆模型并不禁止这种行为。你引用的部分是Java试图说,“看,我们需要发生 - 之前的一致性,但这里的另一个奇怪的事情仍然不应该发生”。在非规范性说明1.10 / 25中,C ++ 14对此问题的看法同样含糊不清。