为什么重新排序会发生两个易变的变量?

时间:2017-06-04 07:44:17

标签: java concurrency volatile java-memory-model

我试图调查在java环境中重新排序的行为(使用JDK 9-ea + 170)并发现了一件我无法解释的事情,所以我很高兴听到一些关于它的说明。这是一个例子:

public class Client {
    int x;
    int y;
    public void test() {
        x++;
        y++;
    }
    public static void main(String[] args) {
        Client c = new Client();
        while(c.y <= c.x) new Thread(() -> c.test()).start();
        System.out.println(c.x + " " + c.y);
    }
}

此程序有test()方法,只增加x和y值。我正在创建新的线程并调用此test(),直到某些内部Java优化不会更改为x++; y++;指令()的顺序。这样我证明重新排序确实发生了。程序在大部分时间结束(这是预期的)。  现在我已经将易挥发性修饰符添加到y:

public class Client {
    int x;
    volatile int y;
    public void test() {
        x++;
        y++;
    }
    public static void main(String[] args) {
        Client c = new Client();
        while(c.y <= c.x) new Thread(() -> c.test()).start();
        System.out.println(c.x + " " + c.y);
    }
}

此程序永远不会结束,因为volatile会保证volatile之前的所有指令都会被刷新到内存中,因此x++;总是在y++;之前执行,并且不可能有y&gt; X。根据我的理解,这也是预期的。但之后我也将{volatile}添加到int x;,现在我再次看到重新排序,所以程序大部分时间结束:

public class Client {
    volatile int x;
    volatile int y;
    public void test() {
        x++;
        y++;
    }
    public static void main(String[] args) {
        Client c = new Client();
        while(c.y <= c.x) new Thread(() -> c.test()).start();
        System.out.println(c.x + " " + c.y);
    }
}

为什么还要在这里进行重新排序?

1 个答案:

答案 0 :(得分:3)

这不是重新排序的证据。事实上,正在发生的事情是++ volatile不是原子的结果。例如,在更新其中一个变量(A)时,请考虑以下两个线程(Bx)的操作交错:

thread A: load x -> temp
thread B: load x -> temp
thread A: temp = temp + 1
thread B: temp = temp + 1
thread A: save temp -> x
thread B: save temp -> x

如果您使用该交错处理这些操作,您将看到您已经丢失了x的计数。这足以让c.y <= c.x偶尔失败。

(&#34;丢失计数&#34;行为也可能发生在y ...这解释了为什么这个实验只在某些时候失败。)