深刻理解Java內存模型JMM與volatile關鍵字

深刻理解Java內存模型JMM與volatile關鍵字

多核併發緩存架構

Java內存模型

Java線程內存模型跟CPU緩存模型相似,是基於CPU緩存模型來創建的,Java線程內存模型是標準化的,屏蔽掉了底層不一樣計算機的區別。java

例子

編寫代碼來分析編程

public class VolatileVisibilityTest {
    private static boolean initFlag = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("等待數據準備..");
                while (!initFlag){

                }
                System.out.println("============數據準備完畢,執行程序邏輯");
            }
        }).start();
        Thread.sleep(2000);
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                prepareData();
            }
        }).start();
    }

    public static void prepareData(){
 ![](https://img2018.cnblogs.com/blog/1330447/201907/1330447-20190710190530342-1378616179.png)

       System.out.println("數據準備中..");
        initFlag = true;
        System.out.println("數據準備完畢!");
    }
}

執行程序,打印結果緩存

並未出現架構

============數據準備完畢,執行程序邏輯

這段結果併發

分析

第一個線程給了initFlag爲false,第二個執行了prepareData()因此initFlag爲true,可是第一個線程中的flag仍是爲false。ide

若是給initFlag加個volatile關鍵字:性能

public class VolatileVisibilityTest {
    private static volatile boolean initFlag = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("等待數據準備..");
                while (!initFlag){

                }
                System.out.println("============數據準備完畢,執行程序邏輯");
            }
        }).start();
        Thread.sleep(2000);

        new Thread(new Runnable() {
            @Override
            public void run() {
                prepareData();
            }
        }).start();
    }
    public static void prepareData(){
        System.out.println("數據準備中..");
        initFlag = true;
        System.out.println("數據準備完畢!");
    }
}

執行程序,返回結果線程

JMM數據原子操做

  • read(讀取):從主內存讀取數據
  • load(載入):將主內存讀取到的數據寫入工做內存
  • use(使用):從工做內存讀取數據來計算
  • assign(賦值):將計算好的值從新賦值到工做內存中
  • strore(存儲):將工做內存數據寫入主內存
  • write(寫入):將store過去的變量值賦值給主內存中的變量
  • lock(鎖定):將主內存變量枷鎖,表示爲線程獨佔狀態
  • unlock(解鎖):將主內存變量解鎖,解鎖後其餘線程能夠鎖定該變量

整個過程以下code

JMM緩存不一致問題

  • 總線枷鎖(性能過低)server

    • CPU從主內存讀取數據到高速緩存,會在總線對這個數據加鎖,這樣其餘CPU無法去讀或寫這個數據,直到這個CPU使用完整數據釋放鎖以後其餘CPU才能讀取該數據。

  • MESI緩存一致性協議

    • 多個CPU從主內存讀取同一個數據到各自的高速緩存,當其中某個CPU修改了緩存裏的數據,該數據會立刻同步回主內存,其餘CPU經過總線嗅探機制能夠感知到數據的變化從而將本身緩存裏的數據失效。

Volatile可見性底層實現原理

  • Volatile緩存可見性實現原理

    • 底層實現主要是經過彙編lock前綴指令,它會鎖定這塊內存區域的緩存並回寫到主內存,此操做被稱爲「緩存鎖定」,MESI緩存一致性協議機制會阻止同時修改被兩個以上處理器緩存的內存區域數據。一個處理器的緩存值經過總線回寫到內存會致使其餘處理器響應的緩存失效。

    Java程序彙編代碼查看

    • -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolatileVisibilityTest.prepareData
    • 須要先下載hsdis-amd64

IDEA這樣設置

顯式出的結果,其中volatile修飾的彙編代碼以下:

0x000000000349eaff: lock add dword ptr [rsp],0h ;*putstatic initFlag
; - com.tugohost.concurrent.VolatileVisibilityTest::prepareData@9 (line 31)

可見性、原子性與有序性

  • 併發編程三大特性:可見性,原子性,有序性
  • Volatile保證可見性與有序性,可是不保證原子性,保證原子性須要藉助synchronized這樣的鎖機制。
相關文章
相關標籤/搜索