最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)編程之關(guān)鍵字volatile的深入解析

 更新時間:2021年09月16日 14:23:09   作者:我叫葉湘?zhèn)? 
提高java的并發(fā)編程,就不得不提volatile關(guān)鍵字,不管是在面試還是實際開發(fā)中volatile都是一個應(yīng)該掌握的技能,這篇文章主要給大家介紹了關(guān)于Java并發(fā)編程之關(guān)鍵字volatile的相關(guān)資料,需要的朋友可以參考下

前言

volatile是研究Java并發(fā)編程繞不過去的一個關(guān)鍵字,先說結(jié)論:

volatile的作用:

        1.保證被修飾變量的可見性

        2.保證程序一定程度上的有序性

        3.不能保證原子性

下面,我們將從理論以及實際的案例來逐個解析上面的三個結(jié)論

一、可見性

 什么是可見性?

舉個例子,小明和小紅去看電影,剛開始兩個人都還沒買電影票,小紅就先去買了兩張電影票,沒有告訴小明。小明以為小紅沒買,所以也去買了兩張電影票,因為他們只有兩個人,所以他們只能用兩張票,這就是小明和小紅他倆電影票的數(shù)量的可見性。

在講解之前,我們簡單的了解一下JVM當(dāng)中運行時數(shù)據(jù)區(qū)的結(jié)構(gòu)

堆內(nèi)存:存放的就是對象,所以它也是JVM當(dāng)中內(nèi)存最大的一區(qū)域

線程私有區(qū):線程中的棧會去從堆當(dāng)中獲取變量的值來進行操作,正是因為是私有化的,所以兩個線程之間的數(shù)據(jù)是不會共享的

元空間:存放靜態(tài)變量以及常量還有被虛擬機加載的類信息

同理,我們可以將小明和小紅看作java當(dāng)中的兩個線程1和2,共有一個變量

public class volatileTest {
    public static boolean flag = false;
 
    public static void main(String[] args) {
        try {
            new Thread(() -> {
                System.out.println("線程1開始");
                //線程1當(dāng)中取反值,當(dāng)flag為true時才會跳出循環(huán)
                while (!flag) {
                }
                System.out.println("線程1結(jié)束");
            }).start();
            Thread.sleep(100);
            new Thread(() -> {
                System.out.println("線程2開始");
                //線程2給flag賦值
                flag = true;
                System.out.println("線程2結(jié)束");
            }).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

該代碼的運行結(jié)果如下:

可以很清楚的看到,只有線程2是跑完了的,但是明明線程2已經(jīng)給flag賦值,線程1并沒有停止循環(huán),這就是flag這個變量沒有可見性,導(dǎo)致線程1一直不停止

解決的方法有兩種

第一:讓每個線程空余時間就去堆同步數(shù)據(jù)(顯然不合理)

第二:使用volatile關(guān)鍵字去修飾變量flag

讓我們加上volatile試試:

這回線程1總算是成功停止了,由此我們可得,volatile是可以讓變量具有可見性的。

學(xué)習(xí)編程不能只知道如何去使用,而是要知道原理,這樣才會有更多的薪資

那么volatile的底層是如何實現(xiàn)的呢?

如上面jvm運行數(shù)據(jù)區(qū)的圖所示,所有的變量都是存在了堆當(dāng)中,而每個線程都是拿到他們的副本進行計算和修改,volatile干了啥事呢,如下圖所示

這里我們介紹一個新的概念,叫總線(各位可以把它理解成進行連接線程和堆內(nèi)存,在計算機的硬件當(dāng)中,也是有總線的,了解的朋友可以把它用相同概念理解一下)。

當(dāng)一個被volatile修飾的變量,在某一個線程當(dāng)中被修改時,總線會監(jiān)聽到這個變動,并且會讓其他線程中的這個變量失效,簡而言之,當(dāng)線程2當(dāng)中堆flag進行了修改,則會導(dǎo)致線程1當(dāng)中的flag失效,就是把這個線程1當(dāng)中的flag刪了。當(dāng)線程1中沒有flag了,它會重新去獲取flag,這個時候,就會使我們的變量flag具有了可見性。

現(xiàn)在我們已經(jīng)知道了,volatile的實行原理,那么它的底層是如何實現(xiàn)的?

眾所周知,java語言加載時  -> class  ->匯編語言 -> 機器語言,因為volatile是個關(guān)鍵字,所以它的底層是一種匯編語法,被volatile修飾的變量其實就是給它加了個一個lock前綴指令。

也就是說,當(dāng)面試官問到我們,如何手寫一個volatile時,我們可以說在編譯的層面,添加一個lock前綴指令相當(dāng)于一個內(nèi)存屏障,它本身會提供三個功能

        1)它會強制堆緩存的修改操作立即寫入主存

        2)如果是寫操作,它會導(dǎo)致其他CPU中對應(yīng)的緩存行無效

        3)它會確保指令重排序時不會吧其它的指令排到內(nèi)存屏障之前的位置,也不會之前的操作拍到內(nèi)存屏障之后

前面兩點很好理解,并且我們也進行了進一步的認證,第三點可能有朋友不太明白,這就引出了我們下一個論點,volatile可以保證一定的有序性

二、有序性

我們看下面三行代碼

int i=1;
int j=2;
i =i++;

在我們的理解當(dāng)中,程序時自上而下運行的,先是第一行,再是第二行等,然而事實上,jvm可能會對代碼進行重排序,比如它可能就會讓上面的這三行代碼變成下面的狀態(tài)

int i=1;        
i =i++;
int j =2;

為什么會進行重排序,目的是讓代碼執(zhí)行的速度更快,當(dāng)然它也不是隨便亂排的,排序的規(guī)則是根據(jù)代碼的依賴性進行的判斷,簡而言之就是在不影響結(jié)果的情況下進行排序,感興趣的朋友可以自行去了解一下

這是java本身對程序保證的有序性,在不影響運行結(jié)果的情況下進行重排序,但是僅限于單線程的情況下,在多線程的情況中,并不能有效地保證程序的有序性

下圖為手寫的一個單例模式,不做過多的贅述,左邊為代碼,右邊為翻譯的字節(jié)碼文件

通過上圖可以很清晰的看出,new OnlyObject這個操作重點分為了四步,

                第一步:創(chuàng)建這個對象

                第二步:調(diào)用這個類的構(gòu)造方法

                第三步:添加指向(就是從私有線程當(dāng)中執(zhí)行堆)

                第四步:加載

由于java對程序的重排序,會使第二步和第三步進行調(diào)換位置,在單線程當(dāng)中不會有任何問題,而在多線程當(dāng)中就有問題了

看下圖代碼

當(dāng)線程1已經(jīng)完成添加指向時,在堆當(dāng)中其實已經(jīng)分配了一個值,但是這時并沒有調(diào)用構(gòu)造方法,所以導(dǎo)致此時這個對象只是一個半成品對象 ,里面并不是我們想要的值。這時線程2走進來,他發(fā)現(xiàn)object并不為空,所以直接返回了,此時的程序跟我們的業(yè)務(wù)并不相符,所以我們需要使用volatile來保證我們的有序性。

總結(jié)

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

相關(guān)文章

  • SpringMVC實戰(zhàn)案例RESTFul實現(xiàn)添加功能

    SpringMVC實戰(zhàn)案例RESTFul實現(xiàn)添加功能

    這篇文章主要為大家介紹了SpringMVC實戰(zhàn)案例RESTFul實現(xiàn)添加功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • java判斷空的實現(xiàn)方法

    java判斷空的實現(xiàn)方法

    字符串判斷空有兩種情況:一個是分配了內(nèi)存但值為空(""),另一個是未分配內(nèi)存(null),判斷方法包括isEmpty()和isBlank(),其中isBlank()還可以過濾空格、制表符,對于列表,判斷不為空可以使用list!=null && !list.isEmpty()或list!=null && list.size()>0
    2024-09-09
  • Java實戰(zhàn)權(quán)限管理系統(tǒng)的實現(xiàn)流程

    Java實戰(zhàn)權(quán)限管理系統(tǒng)的實現(xiàn)流程

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SpringBoot+MyBatis+AOP+LayUI+Mysql實現(xiàn)一個權(quán)限管理系統(tǒng),大家可以在過程中查缺補漏,提升水平
    2022-01-01
  • SpringBoot整合Canal+RabbitMQ監(jiān)聽數(shù)據(jù)變更詳解

    SpringBoot整合Canal+RabbitMQ監(jiān)聽數(shù)據(jù)變更詳解

    在現(xiàn)代分布式系統(tǒng)中,實時獲取數(shù)據(jù)庫的變更信息是一個常見的需求,本文將介紹SpringBoot如何通過整合Canal和RabbitMQ監(jiān)聽數(shù)據(jù)變更,需要的可以參考下
    2024-12-12
  • JVM與操作系統(tǒng)之間的關(guān)系詳解

    JVM與操作系統(tǒng)之間的關(guān)系詳解

    JVM與操作系統(tǒng)之間是依賴與被依賴的關(guān)系,JVM依賴于操作系統(tǒng)提供的資源和服務(wù),同時JVM也起到了抽象與隔離的作用,為Java程序提供了一個統(tǒng)一的、與平臺無關(guān)的運行環(huán)境,提高了Java程序的安全性
    2025-03-03
  • 詳解Mybatis-plus(MP)中CRUD操作保姆級筆記

    詳解Mybatis-plus(MP)中CRUD操作保姆級筆記

    本文主要介紹了Mybatis-plus(MP)中CRUD操作保姆級筆記,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • springboot項目如何引用公共模塊的bean

    springboot項目如何引用公共模塊的bean

    這篇文章主要介紹了springboot項目如何引用公共模塊的bean問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • springboot Mongodb的集成與使用實例詳解

    springboot Mongodb的集成與使用實例詳解

    這篇文章主要介紹了springboot Mongodb的集成與使用實例詳解,需要的朋友可以參考下
    2018-04-04
  • 詳解JAVA 反射機制

    詳解JAVA 反射機制

    這篇文章主要介紹了JAVA 反射機制的相關(guān)知識,文中講解的非常細致,代碼幫助大家更好的理解學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • maven私服的配置使用方法

    maven私服的配置使用方法

    這篇文章主要介紹了maven私服的配置使用方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評論

邯郸县| 三江| 东阿县| 灵川县| 新巴尔虎左旗| 梧州市| 察哈| 雷州市| 惠来县| 张掖市| 米易县| 平阳县| 肥城市| 璧山县| 门头沟区| 通道| 沙雅县| 广德县| 河北省| 温泉县| 昌邑市| 镇坪县| 高清| 南昌县| 贵南县| 沾益县| 阿拉善左旗| 阿合奇县| 抚顺市| 永定县| 泰来县| 宁安市| 尉氏县| 井研县| 南江县| 获嘉县| 平乐县| 泗洪县| 昭觉县| 南召县| 石门县|