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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (16)

JMM中的因果关系似乎是其中最混乱的部分。我有几个关于JMM因果关系的问题,并允许并发程序中的行为。

据我所知,目前的JMM总是禁止因果关系循环。(我对吗?)

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

原来 x = y = 0

r3 = x; if (r3 == 0) x = 42; r1 = x; y = r1;

r2 = y; x = r2;

直觉上,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中这个执行订单怎么合法呢?

提问于
用户回答回答于

正如所解释的,唯一写入的值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;

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

关于优化的一般注意事项:我认为您熟悉从主内存读取时涉及的性能损失。这就是为什么JIT编译器倾向于拒绝尽可能地去做,在这个例子中,事实证明它并不需要阅读x才能知道要写什么y

上记号的一般注意事项:r1r2r3局部变量(他们可能是在堆栈或CPU寄存器); xy共享变量(这些都在主内存中)。没有考虑到这些,这些例子就没有意义了。

用户回答回答于

编译器可以执行一些分析和优化,并以Thread1的以下代码结束:

y=42; // step 1
r3=x; // step 2
x=42; // step 3

对于单线程执行,此代码等同于原始代码,因此是合法的。然后,如果Thread2的代码在步骤1和步骤2之间执行(这很可能),那么r3也被分配42。

此代码示例的整个概念是为了演示正确同步的需要。

扫码关注云+社区