8

根据 Java 语言规范(示例 17.4-1),以下代码段(从 开始A == B == 0)...

Thread 1             Thread 2
--------             --------
r2 = A;              r1 = B;
B = 1;               A = 2;

... 可能导致r2 == 2r1 == 1。这是因为执行的结果B = 1;不取决于是否r2 = A已经执行,因此JVM可以自由地交换这两条指令的执行顺序。换句话说,规范允许以下交织:

Thread 1             Thread 2
--------             --------
B = 1;
                     r1 = B;
                     A = 2;
r2 = A;

这显然导致r2 == 1r1 == 1

我的问题:

假设我们稍微调整一下这个例子:

Thread 1             Thread 2
--------             --------
r2 = A;              r1 = B;
monitorenter obj     monitorenter obj
monitorexit obj      monitorexit obj
B = 1;               A = 2;

whereobj是线程之间共享的引用。

r2 = A重新排序是否B = 1仍然允许?


JLS 说……

但是,允许编译器对任一线程中的指令重新排序,只要这不会影响该线程的单独执行。

...这表明指令仍可能被交换。另一方面,以下声明

监视器上的解锁发生在该监视器上的每个后续锁定之前。

表明在某些调度下,我们可能在两个线程中的语句之间存在发生前的关系,这可能不允许指令重新排序。

4

1 回答 1

3

非正式地,这是不允许的。这被称为“蟑螂汽车旅馆模式”

http://jeremymanson.blogspot.com/2007/05/roach-motels-and-java-memory-model.html

特别是,不能跨同步块移动动作。

然而,在形式上,JMM 并没有谈到重新排序。在你的例子中,我们只能推断,

  1. 在总同步顺序中,同步块 1 在同步块 2 之前,因此r2=A发生在之前A=2;必须为 0。但和r2之间没有约束;可以是 0 或 1。B=1r1=Br1

  2. 或相反。r1必须为 0,r2可以是 0 或 2。

所以程序仍然包含数据竞争;尽管如此,我们可以推断(r1,r2)只能是(0,0),(1,0),(0,2);不可能是 (1,2)

于 2013-01-03T21:28:31.050 回答