简体   繁体   English

挥发性 Java 重新排序

[英]Volatile Java reordering

Firstly let me say that I am aware of this being a fairly common topic here but searching for it I couldn't quite find another question that clarifies the following situation.首先让我说,我知道这是一个相当普遍的话题,但在搜索它时,我找不到另一个可以澄清以下情况的问题。 I am very sorry if this is a possible duplicate but here you go:如果这可能是重复的,我很抱歉,但在这里你 go:

I am new to concurrency and have been given the following code in order to answer questions:我是并发新手,为了回答问题,我得到了以下代码:

  • a) Why any other output aside from "00" would be possible? a) 为什么除了 "00" 之外的任何其他 output 都是可能的?
  • b) How to amend the code so that "00" will ALWAYS print. 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();
    }

For a ) my answer would be the following: In the absence of any volatile / synchronization construct the compiler could reorder some of the instructions.对于a ) 我的回答如下:在没有任何 volatile / 同步构造的情况下,编译器可以重新排序一些指令。 In particular, "this.initialInt = val;"特别是“this.initialInt = val;” and "this.flag = true;"和“this.flag = true;” could be switched so that this situation could occur: The threads are both started and t1 charges ahead.可以切换,以便发生这种情况:线程都已启动并且 t1 提前收费。 Given the reordered instructions it first sets flag = true.给定重新排序的指令,它首先设置 flag = true。 Now before it reaches the now last statement of "this.initialInt = val;"现在在它到达“this.initialInt = val;”的最后一条语句之前the other thread jumps in, checks the if-condition and immediately returns thus printing the unchanged initialInt value of 1. Besides this, I believe that without any volatile / synchronization it is not for certain whether t2 might see the assignment performed to initialInt in t1 so it may also print "1" as the default value.另一个线程跳进来,检查 if 条件并立即返回,从而打印未更改的 initialInt 值 1。除此之外,我相信如果没有任何 volatile / 同步,不确定 t2 是否会看到在 t1 中对 initialInt 执行的分配所以它也可以打印“1”作为默认值。

For b ) I think that flag could be made volatile.对于b )我认为该标志可能会变得不稳定。 I have learned that when t1 writes to a volatile variable setting flag = true then t2, upon reading out this volatile variable in the if-statement will see any write operations performed before the volatile write, hence initialInt = val, too.我了解到,当 t1 写入 volatile 变量设置 flag = true 然后 t2 时,在 if 语句中读取此 volatile 变量时,将看到在 volatile 写入之前执行的任何写操作,因此 initialInt = val 也是。 Therefore, t2 will already have seen its initialInt value changed to 0 and must always print 0. This will only work, however, if the use of volatile successfully prevents any reordering as I described in a).因此,t2 已经看到它的 initialInt 值更改为 0,并且必须始终打印 0。但是,这只有在使用 volatile 成功地阻止了我在 a) 中描述的任何重新排序时才有效。 I have read about volatile accomplishing such things but I am not sure whether this always works here in the absence of any further synchronized blocks or any such locks.我已经阅读过有关 volatile 完成此类事情的信息,但我不确定在没有任何进一步同步块或任何此类锁的情况下这是否总是有效。 From this answer I have gathered that nothing happening before a volatile store (so this.flag = true) can be reordered as to appear beyond it.这个答案中,我收集到在 volatile 存储(因此 this.flag = true)之前没有发生任何事情,可以重新排序以使其出现在它之外。 In that case initialInt = val could not be moved down and I should be correct, right?在那种情况下,initialInt = val 不能向下移动,我应该是正确的,对吧? Or not?或不? :) :)

Thank you so much for your help.非常感谢你的帮助。 I am looking forward to your replies.我期待着您的回复。

This example will alway print 00, because you do changeVal(0) before the printing.此示例将始终打印 00,因为您在打印之前执行了changeVal(0)

to mimic the case where 00 might not be printed, you need to move initialInt = 1;要模拟可能不打印 00 的情况,您需要移动initialInt = 1; to the context of a thread like so:像这样的线程的上下文:

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

now you might have a race condition, that sets initialInt back to 1 in thread1 before it is printed in thread2现在你可能有一个竞争条件,它在 thread1 中将 initialInt 设置回 1,然后在 thread2 中打印

another alternative that might results in a race-condition but is harder to understand, is switching the order of setting the flag and setting the value另一种可能导致竞争条件但更难理解的替代方法是切换设置标志和设置值的顺序

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

There are no explicit synchronizations, so all kinds of interleavings are possible, and changes made by one thread are not necessarily visible to the other so, it is possible that the changes to flag are visible before the changes to initialInt , causing 10 or 01 output, as well as 00 output.没有显式同步,因此各种交织都是可能的,并且一个线程所做的更改不一定对另一个线程可见,因此,对flag的更改可能在更改initialInt之前可见,导致 10 或 01 output ,以及 00 output。 11 is not possible, because operations performed on variables are visible to the thread performing them, and effects of changeVal(0) will always visible for at least one of the threads. 11 是不可能的,因为对变量执行的操作对执行它们的线程是可见的,并且changeVal(0)的效果对于至少一个线程总是可见的。

Making changeVal synchronized, or making flag volatile would fix the issue.使changeVal同步,或使flag volatile 可以解决问题。 flag is the last variable changed in the critical section, so declaring it as volatile would create a happened-before relationship, making changes to initialInt visible. flag是临界区中最后一个更改的变量,因此将其声明为volatile将创建发生前的关系,从而使initialInt的更改可见。

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

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