簡體   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