【问题标题】:Why reordering takes place with two volatile variables?为什么使用两个 volatile 变量进行重新排序?
【发布时间】:2017-06-04 07:44:16
【问题描述】:

我正在尝试调查在 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);
    }
}

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

【问题讨论】:

    标签: java concurrency volatile java-memory-model


    【解决方案1】:

    这不是重新排序的证据。事实上,正在发生的事情是++ 上的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 &lt;= c.x 偶尔失败。

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

    【讨论】:

    • 您能否举例说明 B > A 的可能性?在您上面描述的流程中,对 A 的分配发生在对 B 的分配之前。
    • 1) 变量为xyAB 表示线程。 2) 所需要发生的就是你在x 上比在y 上失去的计数更多,然后y &lt;= x 将是false
    • 感谢更新!现在它对我来说有点干净了。斯蒂芬,现在我正在运行相同的代码,但在 test() 上使用了同步关键字。当 volatile 在 y 或 x 和 y 上时它可以正常工作,但在没有 volatile 的情况下失败(这是预期的),并且在 volatile 仅在 x 上时失败。这是正常行为吗?如果你知道发生了什么请解释一下
    • 是的。这是正常的。当您访问非易失性变量而不在互斥体中执行此操作时,您可能会看到该变量的陈旧值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    • 2023-01-26
    • 2022-06-13
    • 1970-01-01
    • 2012-03-07
    相关资源
    最近更新 更多