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

Java JLS是否允许此指令重新排序?,java,jvm,memory-model,jls,Java,Jvm,Memory Model,Jls,根据Java语言规范()以下代码段(从A==B==0开始) 。。。可能导致r2==2和r1==1。这是因为执行B=1的结果不取决于是否执行了r2=A,因此JVM可以自由地交换这两条指令的执行顺序。换句话说,规范允许以下交错: Thread 1 Thread 2 -------- -------- B = 1; r1 = B; A = 2; r2 = A; 这显然会

根据Java语言规范()以下代码段(从
A==B==0
开始)

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

Thread 1             Thread 2
--------             --------
B = 1;
                     r1 = B;
                     A = 2;
r2 = A;
这显然会导致
r2==1
r1==1

我的问题: 假设我们稍微调整一下示例:

Thread 1             Thread 2
--------             --------
r2 = A;              r1 = B;
monitorenter obj     monitorenter obj
monitorexit obj      monitorexit obj
B = 1;               A = 2;
其中,
obj
是线程之间共享的引用

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


JLS说

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

…哪种类型表示指令仍然可以交换。另一方面,以下陈述

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


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

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

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

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

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

  • 或者反过来说<代码>r1必须为0,
    r2
    可以为0或2


  • 因此,该程序仍然包含数据竞争;然而,我们可以推断
    (r1,r2)
    只能是(0,0),(1,0),(0,2);不可能是(1,2)

    这是否取决于监控代码?如果我理解正确的话,这些监控行无论如何都可以优化,因为零代码至少在理论上没有影响和重叠。如果它们不影响语义,那么当然,它们可能会被删除。但是如果它们阻止指令重新排序,它们确实会影响语义,对吗?是的,我就是这样理解第一个qouted语句的:即使被监视的块被重新排序执行,语义在独立执行中也不会改变。第二种说法是,锁所有权的转换是无缝的。我对整个重新排序的理解如下:重新排序仅限于语义。连锁没有不良影响。但指令重新排序在应用于代码时会导致(甚至更多)未定义的行为,代码共享变量而没有任何联锁机制。
    Thread 1             Thread 2
    --------             --------
    r2 = A;              r1 = B;
    monitorenter obj     monitorenter obj
    monitorexit obj      monitorexit obj
    B = 1;               A = 2;