繁体   English   中英

挥发性 Java 重新排序

[英]Volatile Java reordering

首先让我说,我知道这是一个相当普遍的话题,但在搜索它时,我找不到另一个可以澄清以下情况的问题。 如果这可能是重复的,我很抱歉,但在这里你 go:

我是并发新手,为了回答问题,我得到了以下代码:

  • a) 为什么除了 "00" 之外的任何其他 output 都是可能的?
  • b) 如何修改代码以便始终打印“00”。
 boolean flag = false;

    void changeVal(int val) {
        if(this.flag){
            return;
        }
        this.initialInt = val;
        this.flag = true;
    }

    int initialInt = 1;

    class MyThread extends Thread {
        public void run(){
            changeVal(0);
            System.out.print(initialInt);
        }
    }

    void execute() throws Exception{
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start(); t2.start(); t1.join(); t2.join();
        System.out.println();
    }

对于a ) 我的回答如下:在没有任何 volatile / 同步构造的情况下,编译器可以重新排序一些指令。 特别是“this.initialInt = val;” 和“this.flag = true;” 可以切换,以便发生这种情况:线程都已启动并且 t1 提前收费。 给定重新排序的指令,它首先设置 flag = true。 现在在它到达“this.initialInt = val;”的最后一条语句之前另一个线程跳进来,检查 if 条件并立即返回,从而打印未更改的 initialInt 值 1。除此之外,我相信如果没有任何 volatile / 同步,不确定 t2 是否会看到在 t1 中对 initialInt 执行的分配所以它也可以打印“1”作为默认值。

对于b )我认为该标志可能会变得不稳定。 我了解到,当 t1 写入 volatile 变量设置 flag = true 然后 t2 时,在 if 语句中读取此 volatile 变量时,将看到在 volatile 写入之前执行的任何写操作,因此 initialInt = val 也是。 因此,t2 已经看到它的 initialInt 值更改为 0,并且必须始终打印 0。但是,这只有在使用 volatile 成功地阻止了我在 a) 中描述的任何重新排序时才有效。 我已经阅读过有关 volatile 完成此类事情的信息,但我不确定在没有任何进一步同步块或任何此类锁的情况下这是否总是有效。 这个答案中,我收集到在 volatile 存储(因此 this.flag = true)之前没有发生任何事情,可以重新排序以使其出现在它之外。 在那种情况下,initialInt = val 不能向下移动,我应该是正确的,对吧? 或不? :)

非常感谢你的帮助。 我期待着您的回复。

此示例将始终打印 00,因为您在打印之前执行了changeVal(0)

要模拟可能不打印 00 的情况,您需要移动initialInt = 1; 像这样的线程的上下文:

class MyThread extends Thread {
        public void run(){
            initialInt = 1;
            changeVal(0);
            System.out.print(initialInt);
        }
    }

现在你可能有一个竞争条件,它在 thread1 中将 initialInt 设置回 1,然后在 thread2 中打印

另一种可能导致竞争条件但更难理解的替代方法是切换设置标志和设置值的顺序

void changeVal(int val) {
        if(this.flag){
            return;
        }
        this.flag = true;
        this.initialInt = val;
 }

没有显式同步,因此各种交织都是可能的,并且一个线程所做的更改不一定对另一个线程可见,因此,对flag的更改可能在更改initialInt之前可见,导致 10 或 01 output ,以及 00 output。 11 是不可能的,因为对变量执行的操作对执行它们的线程是可见的,并且changeVal(0)的效果对于至少一个线程总是可见的。

使changeVal同步,或使flag volatile 可以解决问题。 flag是临界区中最后一个更改的变量,因此将其声明为volatile将创建发生前的关系,从而使initialInt的更改可见。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM