併發編程系列之什麼是Java內存模型?

併發編程系列之變量可見性問題探究

1、什麼是併發中的變量可見性問題

以例子的形式看看,定義一個變量,先用static修飾,在主線程修改之後,看看在新開的子線程裏能被看到?

public class Example {
private static boolean flag = true;
    
    public void testss() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (IfTest.flag) {
                        i++;
                }
                System.out.println(i);
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        IfTest.flag = false;
        System.out.println("設置flag");
    }
}

執行,控制檯打印:

設置flag

ps:主線程對flag變量進行修改,子線程是不能看到的,所以裏面一直在循環,不能打印統計數據值。然後怎麼才能讓併發線程看見?

  • 方式1:使用volatile關鍵字
public class Example {
private static volatile boolean flag = true;
    
    public void testss() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (IfTest.flag) {
                        i++;
                }
                System.out.println(i);
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        IfTest.flag = false;
        System.out.println("設置flag");
    }
}

控制檯打印:

設置flag
72071943

  • 方式2:使用synchronized同步鎖
public class Example {
private static boolean flag = true;
    
    public void testss() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (IfTest.flag) {
                    synchronized (this) {
                        i++;
                    }
                }
                System.out.println(i);
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        IfTest.flag = false;
        System.out.println("設置flag");
    }
}

控制檯打印:

設置flag
86726163

2、什麼是Java內存模型?

解答這個問題,需要涉及到Java的內存模型,如下所示,Java內存模型及操作規範:

  1. 共享變量都是放在主內存中的
  2. 每個線程都有自己的工作內存,線程只可操作自己的工作內存
  3. 線程要操作共享變量,需要從主內存中讀取到工作內存,改變值之後要從工作內存同步到主內存
  • Java內存模型的同步交換協議,規定了8種原子操作

原子操作:不可被中斷的一個或一系列操作

  1. lock(鎖定):將主內存中的變量鎖定,爲一個線程所獨佔
  2. unlock(解鎖):將lock加的鎖解除,其他的線程有機會訪問此變量
  3. read(讀取):作用於主內存變量,將主內存中的變量值讀取到工作內存
  4. load(加載):作用於工作內存,將read讀取到的值保存到工作內存中的變量副本
  5. use(使用):作用於工作內存變量,將值傳遞給線程的代碼執行引擎
  6. assign(賦值):作用於工作內存變量,將執行引擎處理返回的值重新賦值給變量副本
  7. store(存儲):作用於工作內存變量,將變量副本的值傳送到主內存中
  8. write(寫入):作用於主內存變量,將store傳送過來的值寫入到主內存的共享變量中
  • Java內存模型的同步交互協議,執行上述8種原子操作時必須滿足如下規則
  1. 不允許read和load,store和write操作之一單獨出現。即不允許加載或同步工作到一半。
  2. 不允許一個線程丟棄它最近的assign操作,即變量在工作內存中改變之後,必須將數據同步回主內存
  3. 不允許一個線程無原因地(無assign操作)將數據從工作內存同步到主內存中。
  4. 一個新的變量可能在主內存中誕生。
  5. 一個變量在同一個時刻只允許一條線程對其進行lock操作,但lock操作可以被同一條線程重複執行多次,多次lock之後必須要執行相同次數unlock操作,變量纔會解鎖
  6. 如果對一個對象進行lock操作,那麼會清空工作內存變量中的值,在執行引擎使用這個變量前,需要重新執行load或assign操作初始變量的值
  7. 如果一個對象事先沒有被lock,就不允許對其進行unlock操作,也不允許去unlock一個被其他線程鎖住的變量。
  8. 對一個變量執行unlock操作之前,必須將此變量同步回主內存中(執行store、write)
  • Java內存模型的同步協議,操作規範
  1. 將一個變量從主內存複製到工作內存要順序執行read、load操作;要將變量從工作內存同步回主內存要用store、write操作。只要求順序執行,不一定是連續執行

圖引用網上資料:


3、保證變量可見性的方法

  1. final變量
  2. synchronized
  3. volatile修飾

4、Synchronized怎麼做到可見性

  • synchronized語義規範:
  1. 進入同步塊前,先清空工作內存中的共享變量,從主內存加載
  2. 解鎖前,必須將修改的共享變量同步回主內存
  • synchronized是如何做到線程安全的?
  1. 鎖機制保護共享資源,只有獲得鎖的線程才能操作共享資源
  2. synchronized語義規範保證了修改共享資源後,會同步回主內存,就做到了線程安全

5、volatile關鍵字解密

  • volatile語義規範:
  1. 使用volatile變量時,必須重新從主內存加載到工作內存,並且read、load是連續的
  2. 修改volatile變量後,必須馬上同步回主內存,並且store、write是連續的
  • volatile可以做到線程安全?
    不能,因爲volatile沒有鎖機制,線程是可以併發操作共享資源的

  • volatile相對synchronized有什麼優點?

  1. 使用volatile比synchronized簡單
  2. volatile性能比synchronized好
  • volatile的使用場景?
  1. volatile只能修飾成員變量
  2. 在多線程併發的場景才使用
  • volatile支持併發編程三大特效?
    併發編程三大特效:原子性、有序性、可見性。
  1. 可見性:volatile和synchronized關鍵字一樣,都可以保證可見性
  2. 有序性:volatile可以保證有序性,避免指令編排的情況,依賴於操作系統的內存屏障
  3. 原子行 :volatile只能保證單個操作的原子性,不能保證一系列操作的原子性,不能保證線程安全,所以說volatile不能保證原則性
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章