volatile的指令重排細節

2021-09-10 21:56:52 字數 1670 閱讀 4808

volatile禁止重排優化

volatile關鍵字另乙個作用就是禁止指令重排優化,從而避免多執行緒環境下程式出現亂序執行的現象,關於指令重排優化前面已詳細分析過,這裡主要簡單說明一下volatile是如何實現禁止指令重排優化的。先了解乙個概念,記憶體屏障(memory barrier)。

記憶體屏障,又稱記憶體柵欄,是乙個cpu指令,它的作用有兩個,一是保證特定操作的執行順序,二是保證某些變數的記憶體可見性(利用該特性實現volatile的記憶體可見性)。由於編譯器和處理器都能執行指令重排優化。如果在指令間插入一條memory barrier則會告訴編譯器和cpu,不管什麼指令都不能和這條memory barrier指令重排序,也就是說通過插入記憶體屏障禁止在記憶體屏障前後的指令執行重排序優化。memory barrier的另外乙個作用是強制刷出各種cpu的快取資料,因此任何cpu上的執行緒都能讀取到這些資料的最新版本。總之,volatile變數正是通過記憶體屏障實現其在記憶體中的語義,即可見性和禁止重排優化。下面看乙個非常典型的禁止重排優化的例子dcl,如下:

/**

* created by zejian on 2017/6/11.

*/public

class

doublechecklock

public

static doublechecklock getinstance()

}}return instance;

}}

上述**乙個經典的單例的雙重檢測的**,這段**在單執行緒環境下並沒有什麼問題,但如果在多執行緒環境下就可以出現執行緒安全問題。原因在於某乙個執行緒執行到第一次檢測,讀取到的instance不為null時,instance的引用物件可能沒有完成初始化。因為instance = new doublechecklock();可以分為以下3步完成(偽**)

memory =

allocate()

;//1.分配物件記憶體空間

instance

(memory)

;//2.初始化物件

instance = memory;

//3.設定instance指向剛分配的記憶體位址,此時instance!=null

由於步驟1和步驟2間可能會重排序,如下:

memory =

allocate()

;//1.分配物件記憶體空間

instance = memory;

//3.設定instance指向剛分配的記憶體位址,此時instance!=null,但是物件還沒有初始化完成!

instance

(memory)

;//2.初始化物件

由於步驟2和步驟3不存在資料依賴關係,而且無論重排前還是重排後程式的執行結果在單執行緒中並沒有改變,因此這種重排優化是允許的。但是指令重排只會保證序列語義的執行的一致性(單執行緒),但並不會關心多執行緒間的語義一致性。所以當一條執行緒訪問instance不為null時,由於instance例項未必已初始化完成,也就造成了執行緒安全問題。那麼該如何解決呢,很簡單,我們使用volatile禁止instance變數被執行指令重排優化即可。

//禁止指令重排優化

private

volatile

static doublechecklock instance;

Volatile禁止指令重排

你寫的程式,計算機並不是按照你寫的那樣去執行的。源 編譯器優化的重排 指令並行可能會重排 記憶體系統可能會重排 執行 處理器在進行指令重排的時候,考慮,資料之間的依賴性!指令重排 不會造成影響的例子 int x 1 1int y 2 2x x 5 3y x x 4我們所期望的 1234 執行的時候可...

Volatile禁止指令重排序(三)

計算機在執行程式時,為了提高效能,編譯器和處理器常常會對指令重排,一般分為以下三種 源 編譯器優化的重排 指令並行的重排 記憶體系統的重排 最終執行指令單執行緒環境裡面確保最終執行結果和 順序的結果一致 處理器在進行重排序時,必須要考慮指令之間的資料依賴性 多執行緒環境中線程交替執行,由於編譯器優化...

一次討論volatile指令重排引發的經歷

public class tcatch exception e i system.out.println m end public static void main string args catch exception e t.runing false 結果會 我一直以為,時間片輪詢,遲早runi...