2013-05-02 251 views
2

在審查this question我注意到這個代碼:這段代碼爲什麼會失敗?

class MyThread extends Thread { 
    private boolean stop = false; 

    public void run() { 
    while(!stop) { 
     doSomeWork(); 
    } 
    } 

    public void setStop() { 
    this.stop = true; 
    } 
} 

但是我不明白爲什麼會這樣失敗。其他線程是否無法訪問「實際」停止變量?

+0

如何調用'setStop()'?來自MyThread的相同實例還是不同? – 2013-05-02 19:18:50

+0

我不知道,不幸的是這是一個抽象的例子,從我鏈接到 – 2013-05-02 19:25:25

+0

的問題setStop會從不同的線程調用。所有運行方法必須做的就是返回,它不需要標誌。 – 2013-05-02 19:53:17

回答

5

實例變量stop需要是易失性的,否則不能保證其他線程會看到它的變化。在工作中有許多相互衝突的興趣:線程想要一致的程序狀態視圖,CPU希望能夠緩存數據,JVM希望能夠重新排序指令。使實例變量volatile變量意味着它不能被緩存,而且會發生這種情況 - 在建立限制指令重新排序的關係之前。

請參閱this other answer(+1),其中列舉了可能會發生的重新排序而不標記變量volatile的一個好例子。

(順便using interruption for thread cancellation最好使用一個實例變量)

+0

你應該提到提升這是失敗的第一個原因。 – 2013-05-02 19:23:33

+0

@JohnVint「懸掛」? – user2246674 2013-05-02 19:24:48

+1

@ user2246674我創建了自己的答案來解決吊裝問題。 – 2013-05-02 19:28:32

0

其他線程不能保證看到停止更新值 - 你需要建立關係「之前發生」。最簡單的方法是停止波動。

1

變量stop必須聲明爲volatile。

雖然我更喜歡使用中斷來停止線程。

6

JIT編譯器可以重新排序讀取並在應用程序中,只要

  1. 的動作順序一致,
  2. 改變的行動不違反線程內語義寫道。

這只是一種奇特的說法,所有的動作看起來應該像只有一個線程執行一樣。所以,你可以得到JIT重新編譯你的代碼看起來像這樣

class MyThread extends Thread { 
    private boolean stop = false; 

    public void run() { 
    if(!stop){ 
     while(true){ 

     } 
    } 
    } 

這就是所謂法律優化提升。它仍然像串行一樣運行,但在使用多線程時提供令人驚訝的結果。

通過聲明字段易失性,您告訴Java不要執行任何重新排序。與Nathan Hughes提到的內存一致性

+0

JVM將檢查'doSomeWork()'是否在提升之前設置了「stop」字段?我認爲是的,否則,優化似乎不是最佳:) – dlev 2013-05-02 19:33:55

+0

@dlev是的,絕對!否則會導致可怕的優化。 – 2013-05-02 19:35:53

+1

偉大的信息,謝謝! – 2013-05-02 19:51:21