1. 程式人生 > >關於volatile關鍵字的一點小總結

關於volatile關鍵字的一點小總結

一、volatile的實現機制

  • 記憶體屏障:
    • 它確保指令重排序時不會把其後面的指令排到記憶體屏障之前的位置,也不會把前面的指令排到記憶體屏障的後面;即在執行到記憶體屏障這句指令時,在它前面的操作已經全部完成;

    • 它會強制將對快取的修改操作立即寫入主存;

    • 如果是寫操作,它會導致其他CPU中對應的快取行無效。

二、有序性

  • 有序性:即程式執行的順序按照程式碼的先後順序執行。
int a = 1;              
boolean b = true;
  • 上面定義了2行程式碼,理論上來說a執行在b前面,但是真實情況不一定,因為JVM會去做一些優化,優化後就不一定a執行在b前面了;volatile會禁止指令重排序所以可以保證有序性。

三、可見性

  • 可見性是指當多個執行緒訪問同一個變數時,一個執行緒修改了這個變數的值,其他執行緒能夠立即看得到修改的值。volatile主要是通過記憶體屏障去實現的。使用volatile關鍵字會強制將修改的值立即寫入主存,然後將其他工作記憶體中的資料宣告為無效資料,當執行緒來取資料時發現自己的資料是無效的,於是就到主存取,實現快取的一致性。所以能夠保證其他執行緒可見性。

四、關於volatile原子性問題

  • 上面說了volatile是具備可見性的,簡單猜想應該是具備原子性的。
  • 只是嚴格意義來講,對任意單個volatile變數的讀/寫具有原子性(如boolean a = true;a=false;),但類似於運算(如i++、i=i*x等等)這種複合操作不具有原子性。
volatile int i = 0;
public void increase(){
   i++;
}
  • 上面的程式碼,假設有2個執行緒去呼叫它自增程式碼,執行緒A,B。2個執行緒都呼叫10次,最後的結果一定會比我們自己算的結果要小。這是為什麼呢?這是因為這(i++)是個複合指令,而volatile記憶體屏障只在變數修改的那一個步驟指令觸發,隨後修改主存的值,把其他執行緒i的快取值標記為無效(Invalid)。

  • i++的複合指令包括:

    • 首先讀取i的值
    • 對i進行+1運算
    • 再給i賦值
  • 可以試想下這樣的場景,執行緒A,B同時執行,此時執行緒A,B獲取到的值都是0;執行緒A執行+1操作,然後賦值,此時記憶體屏障生效,把主存的值修改,並把其他執行緒i的快取值標記為無效;執行緒B也是同樣的(注意執行緒B i的值也是0),執行緒B再執行+1操作,然後賦值,此時記憶體屏障生效,把主存的值修改,並把其他執行緒i的快取值標記為無效。這樣子下去結果肯定會比我們自己算的要小。所以在運算的時候volatile保證不了原子性。

到這兒基本結束了,另外文章程式碼或者我理解有誤的地方,希望能批評指出。