一、通程序式看現象
在開始為大家講解Java 多執行緒快取模型之前,我們先看下面的這一段代碼,這段代碼的邏輯很簡單:主執行緒啟動了兩個子執行緒,一個執行緒1、一個執行緒2,執行緒1先執行,sleep睡眠2秒鐘之后執行緒2執行,兩個執行緒使用到了一個共享變數shareFlag,初始值為false,如果shareFlag一直等于false,執行緒1將一直處于死回圈狀態,所以我們在執行緒2中將shareFlag設定為true,
public class VolatileTest {
public static boolean shareFlag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
System.out.print("開始執行執行緒1 =>");
while (!shareFlag){ //shareFlag = false則一直死回圈
//System.out.println("shareFlag=" + shareFlag);
}
System.out.print("執行緒1執行完成 =>");
}).start();
Thread.sleep(2000);
new Thread(() -> {
System.out.print("開始執行執行緒2 =>");
shareFlag = true;
System.out.print("執行緒2執行完成 =>");
}).start();
}
}
如果你沒有學過JMM執行緒模型,可能你看完上面的代碼,希望得到的輸出結果是下面這樣的:
開始執行執行緒1 =>開始執行執行緒2 =>執行緒2執行完成 =>執行緒1執行完成=>
如下圖所示,正常人理解這段代碼,首先執行執行緒1進入回圈,執行緒2修改shareFlag=true,執行緒1跳出回圈,所以跳出回圈的執行緒1會列印"執行緒1執行完成=>",但是經過筆者實驗,"執行緒1執行完成=>"不會被列印,執行緒1也沒有跳出死回圈,這是為什么呢?

二、為什么會產生這種現象(JMM模型)?
要解釋上面提到的問題,我們就需要學習JMM(Java Memory Model)Java 記憶體模型,筆者覺得叫做Java多執行緒記憶體模型更準確一些,

- 首先,在JMM中每個執行緒有自己的作業記憶體,在程式啟動的時候,執行緒將共享變數加載(read&load)到自己的作業記憶體中,加載到執行緒作業記憶體中的記憶體變數是主記憶體中共享變數的副本,也就是說此時shareFlag在記憶體中有三份,值都等于false,
- 當執行緒2執行
shareFlag=true的時候將其作業記憶體副本修改為shareFlag=true,同時將副本的值同步寫回(store&write)到主記憶體中, - 但是執行緒1的作業記憶體中的
shareFlag=false沒有發生變化,所以執行緒1一直處于死回圈之中,
三、MESI 快取一致性協議
按照上文的實驗以及JMM模型,執行緒2修改的共享變數的值,執行緒1感知不到,那怎么樣才能讓執行緒1感知到共享變數的值發生了變化呢?其實也很簡單,給shareFlag共享變數加上volatile關鍵字就可以了,
public volatile static boolean shareFlag = false;
其底層原理是這樣的,加上volatile關鍵字提示JMM遵循MESI 快取一致性協議,該協議包含如下的快取使用規范(看不懂可以不看,下文會用簡單的語言及例子描述一下),
- Modified:代表當前Cache行的資料是修改過的(Dirty),并且只在當前CPU的Cache中是修改過的;此時該Cache行的資料與其他Cache中的資料不同,與記憶體中該行的資料也不同,
- Exclusive:代表當前Cache行的資料是有效資料,其他CPU的Cache中沒有這行資料;并且當前Cache行資料與記憶體中的資料相同,
- Shared:代表多個CPU的Cache中均快取有這行資料,并且Cache中的資料與記憶體中的資料一致;
- Invalid:表示當前Cache行中的資料無效;

上文中的快取使用規范可能過于復雜,簡單的說就是
- 當執行緒2修改shareFlag的時候(參考Modify),告知bus總線我修改了共享變數shareFlag,
- 執行緒1對Bus總線進行監聽,當它獲知共享變數shareFlag發生了修改就會將自己作業記憶體中的shareFlag副本洗掉使其失效,
- 當執行緒1再次需要使用到shareFlag的時候,發現作業記憶體中沒有shareFlag變數副本,就會重新從主記憶體中加載(read&load)
推薦閱讀《并發編程專欄》
歡迎關注我的博客,更多精品知識合集
本文轉載注明出處(必須帶連接,不能只轉文字):字母哥博客 - zimug.com
覺得對您有幫助的話,幫我點贊、分享!您的支持是我不竭的創作動力!,另外,筆者最近一段時間輸出了如下的精品內容,期待您的關注,
- 《kafka修煉之道》
- 《手摸手教你學Spring Boot2.0》
- 《Spring Security-JWT-OAuth2一本通》
- 《實戰前后端分離RBAC權限管理系統》
- 《實戰SpringCloud微服務從青銅到王者》
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/472430.html
標籤:Java
