在
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.但是按照这个顺序,y = 1在r1 = x之后.我在哪里弄错了例子?我该如何正确解释这个例子?
解决方法
我相信这个例子在Java规范中的观点是Hans Boehm等人写在
Outlawing Ghosts年,它指出了一些当代语言的记忆模型(Java,C 11,甚至C 14,仍然解决,但没有解决这个问题)的缺陷.
要点是这样:程序按照书面的规则被语言的规则正确同步. (在C中,如果你使用原子变量和memory_order_relaxed到处都是如此)但是,仍然不会发生意外行为.说明Boehm:机器可以推测x的值,例如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对这个问题也有同样的含糊的意见.