为什么在Java内存模型中允许这种行为?

gag*_*nbm 16 java concurrency compiler-optimization causality java-memory-model

JMM中的因果关系似乎是其中最令人困惑的部分.我有几个关于JMM因果关系的问题,并允许并发程序中的行为.

据我了解,目前的JMM总是禁止因果关系循环.(我对吗?)

现在,根据JSR-133文档,第24页,图16,我们有一个例子,其中:

原来 x = y = 0

线程1:

r3 = x;
if (r3 == 0)
    x = 42;
r1 = x;
y = r1;
Run Code Online (Sandbox Code Playgroud)

线程2:

r2 = y;
x = r2;
Run Code Online (Sandbox Code Playgroud)

直觉上,r1 = r2 = r3 = 42似乎不可能.但是,它不仅被提及,而且在JMM中也被"允许".

对于这种可能性,我无法理解的文件中的解释是:

编译器可以确定分配给的唯一值x是0和42.从那以后,编译器可以推断出,在我们执行的时候r1 = x,我们刚刚执行了42的写入 x,或者我们刚刚读过x和看过价值42.在任何一种情况下,读取x价值42 是合法的.然后可以r1 = x改为r1 = 42; 这将允许y = r1转换为y = 42更早并执行,从而导致相关行为.在这种情况下,y首先提交写入.

我的问题是,它真的是什么样的编译器优化?(我是编译器无知的.)由于42只被写入有条件,当if语句是满意的,编译器如何能决定使用的写作x

其次,即使编译器进行了这种推测性优化,然后提交y = 42然后最终生成r3 = 42,是不是违反了因果关系循环,因为现在没有因果关系的区别?

实际上,在同一文档(第15页,图7)中有一个例子,其中提到类似的因果循环是不可接受的.

那么为什么这个执行命令在JMM中是合法的?

Mar*_*nik 6

如上所述,唯一写入的值x是0和42.线程1:

r3 = x; // here we read either 0 or 42
if (r3 == 0)
  x = 42;  
// at this point x is definitely 42
r1 = x;
Run Code Online (Sandbox Code Playgroud)

因此,JIT编译器可以重写r1 = xr1 = 42,进一步y = 42.关键是,线程1将始终无条件地写入42 y.该r3变量实际上是冗余的,可以从机器代码中完全消除.因此,在该示例代码只给出了一个因果箭头从外观xy,但详细的分析表明,其实并不存在因果关系.令人惊讶的结果是写入y可以提前提交.

关于优化的一般说明:我认为你熟悉从主存储器中读取所涉及的性能损失.这就是为什么JIT编译器会尽可能拒绝这样做的原因,并且在这个例子中,事实证明它实际上并不需要读取x以便知道要写入什么y.

上记号的一般注意事项:r1,r2,r3局部变量(他们可能是在堆栈或CPU寄存器); x,y共享变量(这些是在主内存中).如果不考虑这一点,这些例子就没有意义.