为什么使用两个 volatile 变量进行重新排序?

Why reordering takes place with two volatile variables?

我正在尝试调查 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 添加了 volatile 修饰符:

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 > 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);
    }
}

为什么重新排序也发生在这里?

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

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 偶尔失败。

("lost count" 行为也可能发生在 y ......这解释了为什么这个实验只是在某些时候失败。)