首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用非易失性编写Java重新排序易失性写入

用非易失性编写Java重新排序易失性写入
EN

Stack Overflow用户
提问于 2017-11-04 14:58:21
回答 1查看 245关注 0票数 4

关于下面的代码段,我有一个问题。结果可能有结果0、1、0(这是用JCStress执行的测试)。那这怎么会发生呢?我认为数据写入(data = 1)应该在写入guard2之前在Actor2中执行(guard2 = 1)。我说的对吗?我问你,因为我读过很多次关于挥发物的指令不会被重新排序。此外,根据这一点:http://tutorials.jenkov.com/java-concurrency/volatile.html编写如下:

JVM不能对易失性变量的读取和写入指令进行重新排序( JVM可能出于性能原因重新排序指令,只要JVM检测到程序行为没有从重新排序中改变)。可以重新排序前后的指令,但不能将易失性读或写与这些指令混合。无论遵循什么指令,都保证在读或写之后会发生易失变量的读或写。

所以如果我们不能重新订购

代码语言:javascript
复制
  public class DoubleVolatileTest {

      volatile int guard1 = 0;
      int data = 0;
      volatile int guard2 = 0;

      @Actor
      public void actor1() {
          guard2 = 1;
          data = 1;
          guard1 = 1;
      }

      @Actor
      public void actor2(III_Result r) {
          r.r1 = guard1;
          r.r2 = data;
          r.r3 = guard2;
      }

  }

提前感谢!

EN

回答 1

Stack Overflow用户

发布于 2019-06-04 13:19:16

首先,这是:

不稳定变量的读写指令不能由JVM重新排序.

意味着易失性本身不能被重新排序(易失性即易失性);但请注意:

JVM可以出于性能原因重新排序指令,只要JVM从重新排序中检测到程序行为没有变化。

通常,关于JVM重新排序的推理是不正确的(可以或不这样做)(我读过关于挥发物的指令不会重新排序……)。重新排序/屏障/etc不是JLS的一部分;相反,它是在happens-before规则的前提下工作的,这是您应该关心的唯一事情。

您的示例确实可以简化,如注释中所说的:

代码语言:javascript
复制
@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "the one we care about")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@JCStressTest
@State
public class VolatileTest {


    private volatile int guard = 0;
    private int x = 0;


    @Actor
    void writeActor() {
        guard = 1; // volatile store

        // your reasoning is that these two operations should be re-ordered
        // unfortunately, this is not correct.

        x = 1; // plain store
    }

    @Actor
    void readActor(II_Result r) {

        r.r1 = x; // plain store
        r.r2 = guard; // plain store

    }
}

运行这将确实会导致1, 0,这意味着x = 1确实是与guard = 1重新排序的;实际上,实际上还可能发生更多其他事情(但为了简单起见,我们称它们为重新排序,尽管这并不是您可以观察[1, 0]的唯一原因)。

用JLS术语来说:在这些操作之间(比如一个典型的易失性存储/易失性负载)之前,您还没有建立过任何发生过的情况--因此,这些操作可以自由浮动。这可能是答案的终结,差不多。更广泛的解释是,volatile (因为您使用了它)是这样说的:

对易失性字段的写入发生在随后对该字段的每一次读取之前。

您没有读取任何volatile guard,所以没有任何保证。另一种解释它的方法是这篇优秀的文章,甚至这一个。但是,即使您确实读过guard,仍然没有任何关于重新排序的保证,因为您的代码的设置方式。

只有当volatile的用法成对时,Thread1才能正确工作,也就是说,Thread1volatile字段执行写操作-- Thread2观察写入。在这种情况下,在按程序顺序写入之前所做的一切都将被Thread2看到(显然是在它看到了这个书面值之后)。或者在代码中:

代码语言:javascript
复制
 public class VolatileTest {

    private volatile int guard = 0;
    private int x = 0;


    @Actor
    void writeActor() {

        // store comes "before" the store to volatile
        // as opposed to the previous example
        x = 1; // plain store
        guard = 1; // volatile store
    }

    @Actor
    void readActor(II_Result r) {

        r.r1 = guard; // plain store
        r.r2 = x; // plain store

    }
}

JLS现在向您保证,如果您看到guard1,那么您也将观察到x to be 1 (这一次不能在guard = 1下面重新排序x = 1)。因此,1, 0现在是非法的,因此在输出中从未见过。

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

https://stackoverflow.com/questions/47112036

复制
相关文章

相似问题

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