Volatile禁止指令重排
計算機在執行程式時候,為了提高性能,編譯器和處理器常常會對編譯器程序優化,一般分為以下三種
源代碼->編輯器優化的重排–>指令并行的重排->内部系統工單重排–>最終執行指令
單線程環境裡面確定最終執行結果和代碼順序的結構一緻
處理器在進行重排時候,必須要考慮指令之間的資料依賴性
多線程環境中線程交替執行,由于編譯器優化重排的存在,兩個線程使用的變量能否安保證一緻性無法确定,結果無法預測
指令重排的例子
public class ResortSeqDemo {
int a= 0;
boolean flag = false;
public void method01() {
System.out.println("進去方法一");
a = 1;
flag = true;
}
public void method02() {
if(flag) {
System.out.println("進去方法二");
a = a + 5;
System.out.println("reValue:" + a);
}
}
}
我們安裝正常的順序,分别調用方法method01()和method02(),安裝這個順序,最終的輸出結果就是a的值為6
但是如果在多線程環境下,方法1和方法2不存在資料依賴性,是以原來執行的順序可能是
a = 1;
flag = true;
a = a + 5;
System.out.println("reValue:" + a);
但是經過編譯器執行重排後,可能會出現這樣的情況
flag = true;
a = a + 5;
System.out.println("reValue:" + a);
a=1;
也就是想咨詢完flag=true之後,另外的一個線程立馬調用方法method02,滿足flag的判斷,最終執行a+5,結果為5,這樣同樣會出現資料不一緻的問題
出現這個問題的原因是,多線程情況下,線程交替執行,由于編輯器優化重排的存在,兩個線程在使用變量能否保證一緻性是無法确定的,結果無法預測。這樣就需要通過volatile來休息,保證線程的安全性
Volatile針對指令重排做了什麼
在字段voaltile針對指令重排做了什麼,我們需要先了解一個概念,記憶體屏障,是一個CPU指令,它的作用有兩個
- 保證特定的操作順序
- 保證某些變量記憶體可見性
由于編譯器和處理器都能執行指令重排的優化,如果在指令間插入一條Memory Barrier則會告訴編譯器和CPU,不管什麼指令都不能和這條Memory Barrier指令重排序,也就是說,通過插入記憶體屏障禁止在記憶體屏障前後的指令執行重排序優化。記憶體屏障的另外一個作用是重新整理各種CPU緩存數,是以任何CPU上的線程都能讀取到這些資料的最新版本
記憶體屏障的作用
- 禁止指令重排
- 重新整理緩存,任何CPU上的線程都能讀取到這些資料的最新版本
- 也就是在volatile的寫和讀的時候,加入屏障,防止出現指令重排
線程安全擷取保證
工作記憶體和主記憶體同步延遲現象導緻可見性問題
- 可以通過synchronise或者volatile關鍵字解決,他們都可以讓一個線程改變的變量立即對其他線程可見
對于指令重排導緻的可見性問題和有序性問題
- 可以使用volatile關鍵字解決
說明參考
文章為看視訊部落格學習過程中總結,友善自己以後好複習
https://www.bilibili.com/video/BV18b411M7xz
http://moguit.cn/#/