天天看點

Volatile指令重排

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指令,它的作用有兩個

  • 保證特定的操作順序
  • 保證某些變量記憶體可見性
Volatile指令重排

由于編譯器和處理器都能執行指令重排的優化,如果在指令間插入一條Memory Barrier則會告訴編譯器和CPU,不管什麼指令都不能和這條Memory Barrier指令重排序,也就是說,通過插入記憶體屏障禁止在記憶體屏障前後的指令執行重排序優化。記憶體屏障的另外一個作用是重新整理各種CPU緩存數,是以任何CPU上的線程都能讀取到這些資料的最新版本

記憶體屏障的作用

  • 禁止指令重排
  • 重新整理緩存,任何CPU上的線程都能讀取到這些資料的最新版本
  • 也就是在volatile的寫和讀的時候,加入屏障,防止出現指令重排
線程安全擷取保證

工作記憶體和主記憶體同步延遲現象導緻可見性問題

  • 可以通過synchronise或者volatile關鍵字解決,他們都可以讓一個線程改變的變量立即對其他線程可見

對于指令重排導緻的可見性問題和有序性問題

  • 可以使用volatile關鍵字解決
說明參考

文章為看視訊部落格學習過程中總結,友善自己以後好複習

https://www.bilibili.com/video/BV18b411M7xz

http://moguit.cn/#/