JLS是否允许此指令重新排序?

aio*_*obe 8 java jvm memory-model jls

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

Thread 1             Thread 2
--------             --------
r2 = A;              r1 = B;
B = 1;               A = 2;
Run Code Online (Sandbox Code Playgroud)

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

Thread 1             Thread 2
--------             --------
B = 1;
                     r1 = B;
                     A = 2;
r2 = A;
Run Code Online (Sandbox Code Playgroud)

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

我的问题:

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

Thread 1             Thread 2
--------             --------
r2 = A;              r1 = B;
monitorenter obj     monitorenter obj
monitorexit obj      monitorexit obj
B = 1;               A = 2;
Run Code Online (Sandbox Code Playgroud)

其中obj是线程之间共享的引用.

是的重新排序r2 = A,并B = 1仍然允许?


JLS说......

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

...哪种类型的指示说明可能仍要交换.另一方面,以下声明

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

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

irr*_*ble 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)