欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java并發(fā)編程之volatile與JMM多線程內(nèi)存模型

 更新時間:2022年05月12日 14:55:35   作者:字母哥哥  
這篇文章主要介紹了Java并發(fā)volatile與JMM多線程內(nèi)存模型,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

圖片

一、通過程序看現(xiàn)象

在開始為大家講解Java 多線程緩存模型之前,我們先看下面的這一段代碼。這段代碼的邏輯很簡單:主線程啟動了兩個子線程,一個線程1、一個線程2。線程1先執(zhí)行,sleep睡眠2秒鐘之后線程2執(zhí)行。兩個線程使用到了一個共享變量shareFlag,初始值為false。如果shareFlag一直等于false,線程1將一直處于死循環(huán)狀態(tài),所以我們在線程2中將shareFlag設(shè)置為true。

public class VolatileTest {
  public static boolean shareFlag = false;
  public static void main(String[] args) throws InterruptedException {
    new Thread(() -> {
      System.out.print("開始執(zhí)行線程1 =>");
      while (!shareFlag){  //shareFlag = false則一直死循環(huán)
        //System.out.println("shareFlag=" + shareFlag);
      }
      System.out.print("線程1執(zhí)行完成 =>");
    }).start();
    Thread.sleep(2000);
    new Thread(() -> {
      System.out.print("開始執(zhí)行線程2 =>");
      shareFlag = true;
      System.out.print("線程2執(zhí)行完成 =>");
    }).start();
  }
}

如果你沒有學(xué)過JMM線程模型,可能你看完上面的代碼,希望得到的輸出結(jié)果是下面這樣的:

開始執(zhí)行線程1 =>開始執(zhí)行線程2 =>線程2執(zhí)行完成 =>線程1執(zhí)行完成=>

如下圖所示,正常人理解這段代碼,首先執(zhí)行線程1進(jìn)入循環(huán),線程2修改shareFlag=true,線程1跳出循環(huán)。所以跳出循環(huán)的線程1會打印"線程1執(zhí)行完成=>",但是經(jīng)過筆者實驗,**"線程1執(zhí)行完成=>"不會被打印,線程1也沒有跳出死循環(huán)**,這是為什么呢?

圖片

二、為什么會產(chǎn)生這種現(xiàn)象(JMM模型)?

要解釋上面提到的問題,我們就需要學(xué)習(xí)JMM(Java Memory Model)Java 內(nèi)存模型,筆者覺得叫做Java多線程內(nèi)存模型更準(zhǔn)確一些。

圖片

  • 首先,在JMM中每個線程有自己的工作內(nèi)存,在程序啟動的時候,線程將共享變量加載(read&load)到自己的工作內(nèi)存中,加載到線程工作內(nèi)存中的內(nèi)存變量是主內(nèi)存中共享變量的副本。也就是說此時shareFlag在內(nèi)存中有三個副本,值都等于false。
  • 當(dāng)線程2執(zhí)行shareFlag=true的時候?qū)⑵涔ぷ鲀?nèi)存副本修改為shareFlag=true,同時將副本的值同步寫回(store&write)到主內(nèi)存中。
  • 但是線程1的工作內(nèi)存中的shareFlag=false沒有發(fā)生變化,所以線程1一直處于死循環(huán)之中。

三、MESI 緩存一致性協(xié)議

按照上文的實驗以及JMM模型,線程2修改的共享變量的值,線程1感知不到。那怎么樣才能讓線程1感知到共享變量的值發(fā)生了變化呢?其實也很簡單,給shareFlag共享變量加上volatile關(guān)鍵字就可以了。

public volatile static boolean shareFlag = false;

其底層原理是這樣的,加上volatile關(guān)鍵字提示JMM遵循MESI 緩存一致性協(xié)議,該協(xié)議包含如下的緩存使用規(guī)范(看不懂可以不看,下文會用簡單的語言及例子描述一下)。

  1. Modified:代表當(dāng)前Cache行的數(shù)據(jù)是修改過的(Dirty),并且只在當(dāng)前CPU的Cache中是修改過的;此時該Cache行的數(shù)據(jù)與其他Cache中的數(shù)據(jù)不同,與內(nèi)存中該行的數(shù)據(jù)也不同。
  2. Exclusive:代表當(dāng)前Cache行的數(shù)據(jù)是有效數(shù)據(jù),其他CPU的Cache中沒有這行數(shù)據(jù);并且當(dāng)前Cache行數(shù)據(jù)與內(nèi)存中的數(shù)據(jù)相同。
  3. Shared:代表多個CPU的Cache中都會緩存有這行數(shù)據(jù),并且Cache中的數(shù)據(jù)與內(nèi)存中的數(shù)據(jù)一致;
  4. Invalid:表示當(dāng)前Cache行中的數(shù)據(jù)無效;

圖片

上文中的緩存使用規(guī)范可能過于復(fù)雜,簡單的說就是

  • 當(dāng)線程2修改shareFlag的時候(參考Modify),告知bus總線我修改了共享變量shareFlag,
  • 線程1對Bus總線進(jìn)行監(jiān)聽,當(dāng)它獲知共享變量shareFlag發(fā)生了修改就會將自己工作內(nèi)存中的shareFlag副本刪除使其失效。
  • 當(dāng)線程1再次需要使用到shareFlag的時候,發(fā)現(xiàn)工作內(nèi)存中沒有shareFlag變量副本,就會重新從主內(nèi)存中加載(read&load)

到此這篇關(guān)于Java并發(fā)-volatile與JMM多線程內(nèi)存模型的文章就介紹到這了,更多相關(guān)java多線程內(nèi)存模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java ThreadLocal的設(shè)計理念與作用

    Java ThreadLocal的設(shè)計理念與作用

    這篇文章主要介紹了Java ThreadLocal的設(shè)計理念與作用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • springboot 使用ThreadLocal的實例代碼

    springboot 使用ThreadLocal的實例代碼

    這篇文章主要介紹了springboot 使用ThreadLocal的實例代碼,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 基于java實現(xiàn)DFA算法代碼實例

    基于java實現(xiàn)DFA算法代碼實例

    這篇文章主要介紹了基于java實現(xiàn)DFA算法代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • Spring實戰(zhàn)之使用注解實現(xiàn)聲明式事務(wù)操作示例

    Spring實戰(zhàn)之使用注解實現(xiàn)聲明式事務(wù)操作示例

    這篇文章主要介紹了Spring實戰(zhàn)之使用注解實現(xiàn)聲明式事務(wù)操作,結(jié)合實例形式詳細(xì)分析了spring使用注解實現(xiàn)聲明式事務(wù)相關(guān)配置、接口實現(xiàn)與使用技巧,需要的朋友可以參考下
    2020-01-01
  • Spring控制bean加載順序使用詳解

    Spring控制bean加載順序使用詳解

    在使用spring框架開發(fā)過程中,我們可能會遇到某個bean被另一個bean依賴,也就是bean-b的創(chuàng)建必須依賴bean-a等問題,類似這樣的場景還有很多,總結(jié)來說,這就涉及到bean的加載順序問題,如何解決呢,本文將給大家列舉出幾種常用的解決方案,需要的朋友可以參考下
    2023-09-09
  • Java Spring動態(tài)生成Mysql存儲過程詳解

    Java Spring動態(tài)生成Mysql存儲過程詳解

    這篇文章主要介紹了Java Spring動態(tài)生成Mysql存儲過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • 使用springboot整合websocket實現(xiàn)群聊教程

    使用springboot整合websocket實現(xiàn)群聊教程

    websocket怎么說呢,就是服務(wù)器可以主動向客戶端發(fā)起對話,下面就是springboot整合websocket實現(xiàn)群聊的操作代碼,一起來看一下get新技能吧
    2021-08-08
  • Mybatis中的config.xml配置文件詳細(xì)解析

    Mybatis中的config.xml配置文件詳細(xì)解析

    這篇文章主要介紹了詳解Mybatis-config.xml配置文件,需要的朋友可以參考下
    2017-12-12
  • Java中的靜態(tài)綁定和動態(tài)綁定詳細(xì)介紹

    Java中的靜態(tài)綁定和動態(tài)綁定詳細(xì)介紹

    這篇文章主要介紹了Java中的靜態(tài)綁定和動態(tài)綁定詳細(xì)介紹,在Java中存在兩種綁定方式,一種為靜態(tài)綁定,又稱作早期綁定,另一種就是動態(tài)綁定,亦稱為后期綁定,需要的朋友可以參考下
    2015-01-01
  • java中不定長參數(shù)的實例用法

    java中不定長參數(shù)的實例用法

    在本篇文章里小編給大家分享的是關(guān)于java中不定長參數(shù)的使用方法以及相關(guān)代碼內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。
    2020-02-02

最新評論