二:併發編程-重排序

上一章說了happens-before原則和as-if-serial 語義,也舉了一個單線程的例子說明happens-before僅僅是要求前一個操做的執行結果對後一個操做可見,而且前一個操做要在後一個操做以前。程序員

同時as-if-serial語義保證了在單線程程序中,無論怎樣重排序,都不會對最終結果產生影響。緩存

as-if-serial 語義把單線程程序保護起來,遵循as-if-serial語義的編譯器,runtime和處理器共同爲編寫單線程的程序員創造了一個幻覺,認爲程序就是在順序執行,as-if-serial 語義使單線程程序不用擔憂重排序的困擾,也無需擔憂內存可見性的問題。多線程

既然重排序既然對單線程沒有影響,咱們看一下重排序對多線程有什麼影響:app

一個典型的例子就是在get 和set操做中,若是多線程同事去get和set以下:線程

class TestGetSet{排序

    int a = 0;內存

    boolean flag = false;get

    public void set(){編譯器

        a=1;    //1編譯

        flag = true;   //2

    }

    public int get(){

        if(flag)  // 3

            return a * a;   //4

    }

}

如今假若有兩個線程,分別線程A和線程B,線程A負責進行set,線程B負責進行get,那麼線程B在執行操做4的時候,可否看到線程A在操做1中對共享變量的改變,答案是不必定

因爲操做1和操做2沒有依賴關係,線程會對操做1和操做2進行重排序,操做3和操做4一樣也沒有依賴關係,也會進行重排序,那麼看一下當操做1和操做2進行重排序後:

在上圖中,就是操做1和操做2進行了重排序,線程A在執行的時候,先把變量設置成了true,隨後線程B讀取這個變量,因爲條件爲真,進行下一步操做,可是此刻線程A尚未把共享變量寫入到主內存中,那麼線程B讀取到的結果就是髒數據,多線程程序的語義就被破壞了

再看一下當操做3和操做4被重排序以後:

在程序中,操做3和操做4存在控制依賴關係,當代碼中存在控制依賴關係的時候,會影響到指令序列執行的並行度。爲此編譯器核處理器會採起猜想執行客服控制相關性對並行度的影響。已處理器的才作爲例,執行線程 B的處理器能夠提早讀取並計算a*a,而後把計算結果臨時保存在一個叫重排序緩衝的硬件緩存中。當接下來判斷條件爲真的時候,再把計算結果寫入變量中。

從上咱們能夠看出來,其實就是對操做3和操做4進行了重排序,重排序在這裏破話了多線程程序的語義!

在單線程的程序中,runtime和處理器遵循as-if-serial原則,即便重排序也不會對計算結果產生影響,可是在多線程中,對存在控制依賴關係的操做進行重排序,可能會改變程序的執行結果。

相關文章
相關標籤/搜索