首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么在Java内存模型中允许这种行为?

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

Stack Overflow用户
提问于 2012-11-07 22:22:32
回答 3查看 508关注 0票数 16

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

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

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

最初的x = y = 0

线程1:

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

线程2:

r2 = y;
x = r2;

从直觉上看,r1 = r2 = r3 = 42似乎是不可能的。然而,它不仅被尽可能地提到,而且在JMM中也被“允许”。

对于这种可能性,我不能理解的文档中的解释是:

编译器可以确定分配给x的唯一值是0和42。从这一点,编译器可以推断,在我们执行r1 = x的时候,要么我们刚刚对x执行了42的写操作,要么我们刚刚读取了x并看到了值42。在任何一种情况下,读取x查看值42都是合法的。然后,它可以将r1 = x更改为r1 = 42;这将允许将y = r1转换为y = 42并更早地执行,从而导致有问题的行为。在这种情况下,首先提交对y的写入。

我的问题是,它到底是一种什么样的编译器优化?(我对编译器一无所知。)由于42是有条件地编写的,所以当满足if语句时,编译器如何决定继续编写x

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

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

那么为什么这个执行顺序在JMM中是合法的呢?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-11-07 23:02:28

如前所述,写入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;

因此,即时编译器可以将r1 = x重写为r1 = 42,并进一步重写为y = 42。关键是,线程1将总是无条件地将 42写到yr3变量实际上是多余的,可以从机器代码中完全删除。因此,示例中的代码只给出了从xy的因果箭头的外观,但详细的分析表明,实际上没有因果关系。令人惊讶的结果是,可以提早提交对y的写入。

关于优化的一般说明:我想您对从主存读取时涉及的性能损失很熟悉。这就是为什么JIT编译器在任何可能的情况下都拒绝这样做的原因,在这个例子中,它实际上不需要读取x来知道要写入y的内容。

关于符号的一般说明:r1r2r3局部变量(它们可以在堆栈上或在CPU寄存器中);xy共享变量(它们在主内存中)。如果不考虑这一点,这些例子就没有意义了。

票数 6
EN

Stack Overflow用户

发布于 2012-11-07 23:06:25

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

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

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

此代码示例的整体思想是演示正确同步的必要性。

票数 3
EN

Stack Overflow用户

发布于 2012-11-07 23:03:36

javac没有在很大程度上优化代码,这是一文不值的。JIT优化了代码,但对代码的重新排序相当保守。CPU可以对执行进行重新排序,并在很小程度上进行分配。

强迫CPU不做指令级优化是相当昂贵的,例如,它可能会使CPU减慢10倍或更多。AFAIK,Java设计者希望指定在大多数CPU上高效工作所需的最低限度的保证。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13271649

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档