天天看點

Java并發--并發程式設計模型、記憶體屏障

并發程式設計模型

現代的處理器使用寫緩沖區臨時儲存向記憶體寫入的資料。寫緩沖區可以保證指令流水線持續運作,它可以避免由于處理器停頓下來等待向記憶體寫入資料而産生的延遲。同時,通過以批處理的方式重新整理寫緩沖區,以及合并寫緩沖區中對同一記憶體位址的多次寫,減少對記憶體總線的占用。

雖然寫緩沖區有這麼多好處,**但每個處理器上的寫緩沖區,僅僅對它所在的處理器可見。**這個特性會對記憶體操作的執行順序産生重要的影響:處理器對記憶體的讀/寫操作的執行順序,不一定與記憶體實際發生的讀/寫操作順序一緻!為了具體說明,請看下面的表:

                                                    處理器操作記憶體的執行結果

Java并發--并發程式設計模型、記憶體屏障

假設處理器A和處理器B按程式的順序并行執行記憶體通路,最終可能得到x=y=0的結果。具

體的原因如下圖:

Java并發--并發程式設計模型、記憶體屏障

這裡處理器A和處理器B可以同時把共享變量寫入自己的寫緩沖區(A1,B1),然後從記憶體中讀取另一個共享變量(A2,B2),最後才把自己寫緩存區中儲存的髒資料重新整理到記憶體中(A3,B3)。當以這種時序執行時,程式就可以得到x=y=0的結果。

從記憶體操作實際發生的順序來看,直到處理器A執行A3來重新整理自己的寫緩存區,寫操作A1才算真正執行了。雖然處理器A執行記憶體操作的順序為:A1→A2,但記憶體操作實際發生的順序卻是A2→A1。此時,處理器A的記憶體操作順序被重排序了(處理器B的情況和處理器A一樣,這裡就不贅述了)。

這裡的關鍵是,由于寫緩沖區僅對自己的處理器可見,它會導緻處理器執行記憶體操作的順序可能會與記憶體實際的操作執行順序不一緻。由于現代的處理器都會使用寫緩沖區,是以現代的處理器都會允許對寫-讀操作進行重排序。

                                                    常見處理器允許的重排序類型的清單。

Java并發--并發程式設計模型、記憶體屏障

注意,表3-2單元格中的“N”表示處理器不允許兩個操作重排序,“Y”表示允許重排序

我們可以看出:常見的處理器都允許Store-Load重排序;常見的處理器都不允許對存在資料依賴的操作做重排序。sparc-TSO和X86擁有相對較強的處理器記憶體模型,它們僅允許對寫-讀操作做重排序(因為它們都使用了寫緩沖區)。

記憶體屏障(memory barriers)

記憶體屏障是一組處理器指令,用于實作對記憶體操作的順序限制。

為了保證記憶體可見性,Java編譯器在生成指令序列的适當位置會插入記憶體屏障指令來禁止特定類型的處理器重排序。JMM把記憶體屏障指令分為4類,如下表:

Java并發--并發程式設計模型、記憶體屏障

StoreLoad Barriers是一個“全能型”的屏障,它同時具有其他3個屏障的效果。現代的多處理器大多支援該屏障(其他類型的屏障不一定被所有處理器支援)。執行該屏障開銷會很昂貴,因為目前處理器通常要把寫緩沖區中的資料全部重新整理到記憶體中(Buffer Fully Flush)。

繼續閱讀