volatile的應用
當你的代碼中出現需要一旦發生修改要保證其他線程立即可見的變量,可以使用volatile修飾
volatile的特性:
保證內存的可見性
阻止編譯器指令重排序與CPU指令重排序
- 保證內存可見性?
volatile之前:第一次使用->從主存讀取變量並存到寄存器–>第二次使用–>直接取寄存器變量
volatile之後:第一次使用->從主存讀取變量並存到寄存器–>第二次使用–>從主存讀取變量並存到寄存器
那麼問題來了,雖然每次都從主存讀,但怎麼保證主存就是最新的呢?如果其他CPU修改但只存在了自己的緩存裏,這個數據不就成了舊的了嗎? 請看下面的MESI協議 - 編譯器指令重排序的小例子
static boolean run = true;
public void static main(String[] args){
//啓動一個線程修改run變量
new Thread(()->{run = false;}).start();
while(run){
/**while循環中沒有做任何事 在-server模式下 編譯器可能會將代碼優化成類似如下
* if(run){
* while(true) {
* ....print("...");
* }
* }
* 在-server模式編譯器的強力優化狀態下 這段代碼會出現邏輯錯誤 所以最好將run修飾爲volatile
*/
....print("...");
}
}
CPU指令的亂序執行
順序執行: 洗水壺–>燒開水–>洗茶壺–>洗茶杯–>拿茶葉–>泡茶
亂序執行: 洗水壺–>(燒開水+洗茶壺+洗茶杯)–>拿茶葉–>泡茶
洗茶壺和洗茶杯沒必要等待燒開水後在執行,所以這兩步與燒開水同時執行
volatile的原理所需的基礎知識
MESI協議(保證Cache的一致性)
狀態 | 表示 |
---|---|
已修改Modified (M) | 緩存行被修改,數據與主存不一樣,緩存行只在當前緩存中。 |
獨佔Exclusive (E) | 緩存行只在當前緩存中,數據與主存一樣。 |
共享Shared (S) | 緩存行也存在於其它緩存中,數據與主存一樣。 |
無效Invalid (I) | 緩存行是無效的 |
-
理解MESI協議狀態
核心1從主存加載了變量a,核心1中的變量a的cache line狀態爲E
核心1從主存加載了變量a,核心1和核心2緩存中變量a的cache line狀態變爲S
核心3從主存加載了變量a並把值設置成了6然後寫回主存,核心2與1中緩存變量a的cache line狀態變成M
隨後當核心1或核心2再次訪問變量a時發現狀態爲M會從主存中重新加載 -
內存屏障
一個CPU指令。在程序運行時,爲了提高執行性能,編譯器和處理器會對指令進行重排序,JMM(Java memory model)爲了保證在不同的編譯器和CPU上有相同的結果,通過插入特定類型的內存屏障來禁止特定類型的編譯器重排序和處理器重排序,插入一條內存屏障會告訴編譯器和CPU:不管什麼指令都不能和這條Memory Barrier後面的指令重排序。 -
CPU緩存
緩存有L1(一級緩存) L2(二級緩存) L3(三級緩存) 等級越小CPU訪問他的速度越快。
CPU訪問速度排序
寄存器 < 一級緩存 < 二級緩存 < 三級緩存 < 內存 -
寄存器
當CPU讀取一個變量或操作一個變量時會先去緩存查找如果不存在再從內存查找最終加載到寄存器中隨後修改或讀取時只需要使用寄存器中的值,修改完成後回寫或直寫或直寫緩衝式的儲存到目標位置 -
緩存的所有權(正確性僅供參考,用於方便理解)
寄存器和L1,L2緩存是核心私有的,只有核心自己可以修改與訪問,L3是所有核心共享(在過去只有高端CPU纔有)。當一個核心修改了自己的L1或L2時其他核心並不知道這個變量已被修改所以需要一個一致性協議(MESI)來保證變量一旦被修改數據也同步到其他核心。
volatile的操作過程
- volatile變量的修改
核心1修改a時會通過總線將結果寫到內存中,此操作需要通過總線修改內存其他核心也可以監聽總線從而得知核心1修改了變量a,隨後把自己緩存中的變量a的cache line狀態從S修改爲M。 - volatile變量的讀取
核心1修改變量a的值爲1
當核心2訪問變量a,首先檢查緩存變量的狀態如果爲M或I則代表有其他核心修改了這個變量,隨後核心2會從主存重新加載
從C++,彙編角度搞懂volatile的實現原理
未完待續
import list:
https://www.jianshu.com/p/195ae7c77afe
https://www.jianshu.com/p/506c1e38a922
https://pdfs.semanticscholar.org/3650/4bc31d3b2c5c00e5bfee28ffc5d403cc8edd.pdf