亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Volatile關(guān)鍵字的使用案例

 更新時(shí)間:2023年05月24日 09:50:45   作者:FighterLiu  
這篇文章主要介紹了Volatile關(guān)鍵字的作用,Volatile關(guān)鍵字的作用主要有兩個(gè),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下

Volatile關(guān)鍵字的作用主要有如下兩個(gè):
1.線程的可見性:當(dāng)一個(gè)線程修改一個(gè)共享變量時(shí),另外一個(gè)線程能讀到這個(gè)修改的值。
2. 順序一致性:禁止指令重排序。

一、線程可見性

我們先通過一個(gè)例子來看看線程的可見性:

public class VolatileTest {
    boolean flag = true;
    public void updateFlag() {
        this.flag = false;
        System.out.println("修改flag值為:" + this.flag);
    }
    public static void main(String[] args) {
        VolatileTest test = new VolatileTest();
        new Thread(() -> {
            while (test.flag) {
            }
            System.out.println(Thread.currentThread().getName() + "結(jié)束");
        }, "Thread1").start();
        new Thread(() -> {
            try {
                Thread.sleep(2000);
                test.updateFlag();
            } catch (InterruptedException e) {
            }
        }, "Thread2").start();
    }
}

打印結(jié)果如下,我們可以看到雖然線程Thread2已經(jīng)把flag 修改為false了,但是線程Thread1沒有讀取到flag修改后的值,線程一直在運(yùn)行

修改flag值為:false

我們把flag 變量加上volatile:

volatile  boolean flag = true;

重新運(yùn)行程序,打印結(jié)果如下。Thread1結(jié)束,說明Thread1讀取到了flage修改后的值

修改flag值為:false

Thread1結(jié)束

說到可見性,我們需要先了解一下Java內(nèi)存模型,Java內(nèi)存模型如下所示:

線程之間的共享變量存儲(chǔ)在主內(nèi)存中(Main Memory)中,每個(gè)線程都一個(gè)都有一個(gè)私有的本地內(nèi)存(Local Memory),本地內(nèi)存中存儲(chǔ)了該線程以讀/寫共享變量的副本。

所以當(dāng)一個(gè)線程把主內(nèi)存中的共享變量讀取到自己的本地內(nèi)存中,然后做了更新。在還沒有把共享變量刷新的主內(nèi)存的時(shí)候,另外一個(gè)線程是看不到的。

如何把修改后的值刷新到主內(nèi)存中的?
現(xiàn)代的處理器使用寫緩沖區(qū)臨時(shí)保存向內(nèi)存寫入的數(shù)據(jù)。寫緩沖區(qū)可以保證指令流水線持續(xù)運(yùn)行,它可以避免由于處理器停頓下來等向內(nèi)存寫入數(shù)據(jù)而產(chǎn)生的延遲。同時(shí),通過以批處理的方式刷新寫緩沖區(qū),以及合并寫緩沖區(qū)中對(duì)同一內(nèi)存地址的多次寫,較少對(duì)內(nèi)存總線的占用。但是什么時(shí)候?qū)懭氲絻?nèi)存是不知道的。

所以就引入了volatile,volatile是如何保證可見性的呢?
在X86處理器下通過工具獲取JIT編譯器生成的匯編指令來查看對(duì)volatile進(jìn)行寫操作時(shí),會(huì)多出lock addl。Lock前綴的指令在多核處理器下會(huì)引發(fā)兩件事情:

  • 將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存。
  • 這個(gè)寫回內(nèi)存的操作會(huì)使其他cpu里緩存了該內(nèi)存地址的數(shù)據(jù)無效。

如果聲明了volatile的變量進(jìn)行寫操作,JVM就會(huì)向處理器發(fā)送一條Lock前綴的指令,將這個(gè)變量所在緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存。但是,就算寫回到內(nèi)存,如果其他處理器緩存的還是舊的,在執(zhí)行操作就會(huì)有問題。所以,在多處理器下,為了保證各個(gè)處理器的緩存是一致的,就會(huì)實(shí)現(xiàn)緩存一致性協(xié)議,每個(gè)處理器通過嗅探在總線傳播的數(shù)據(jù)來檢查自己緩存的值是不是過期了,當(dāng)處理器發(fā)現(xiàn)自己緩存行對(duì)應(yīng)的內(nèi)存地址被修改,就會(huì)將當(dāng)前處理器的緩存行設(shè)置成無效狀態(tài),當(dāng)處理器對(duì)這個(gè)數(shù)據(jù)進(jìn)行修改操作的時(shí)候,會(huì)重新從系統(tǒng)內(nèi)存中把數(shù)據(jù)讀到處理器緩存里。

二、順序一致性

在執(zhí)行程序時(shí),為了提高性能,編譯器和處理器常常會(huì)對(duì)指令做重排序。重排序分為如下三種:

1屬于編譯器重排序,2和3屬于處理器重排序。這些重排序可能會(huì)導(dǎo)致多線程程序出現(xiàn)內(nèi)存可見性問題。
當(dāng)變量聲明為volatile時(shí),Java編譯器在生成指令序列時(shí),會(huì)插入內(nèi)存屏障指令。通過內(nèi)存屏障指令來禁止重排序。
JMM內(nèi)存屏障插入策略如下:
在每個(gè)volatile寫操作的前面插入一個(gè)StoreStore屏障,后面插入一個(gè)StoreLoad屏障。
在每個(gè)volatile讀操作后面插入一個(gè)LoadLoad,LoadStore屏障。

Volatile寫插入內(nèi)存屏障后生成指令序列示意圖:

Volatile讀插入內(nèi)存屏障后生成指令序列示意圖:

通過上面這些我們可以得出如下結(jié)論:編譯器不會(huì)對(duì)volatile讀與volatile讀后面的任意內(nèi)存操作重排序;編譯器不會(huì)對(duì)volatile寫與volatile寫前面的任意內(nèi)存操作重排序。

防止重排序使用案例:

public class SafeDoubleCheckedLocking {
    private volatile static Instance instane;
    public  static Instance getInstane(){
        if(instane==null){
            synchronized (SafeDoubleCheckedLocking.class){
                if(instane==null){
                    instane=new Instance();
                }
            }
        }
        return instane;
    }
}

創(chuàng)建一個(gè)對(duì)象主要分為如下三步:

  • 分配對(duì)象的內(nèi)存空間。
  • 初始化對(duì)象。
  • 設(shè)置instance指向內(nèi)存空間。

如果instane 不加volatile,上面的2,3可能會(huì)發(fā)生重排序。假設(shè)A,B兩個(gè)線程同時(shí)獲取,A線程獲取到了鎖,發(fā)生了指令重排序,先設(shè)置了instance指向內(nèi)存空間。這個(gè)時(shí)候B線程也來獲取,instance不為空,這樣B拿到了沒有初始化完成的單例對(duì)象(如下圖)

二、Volatile與Synchronized比較

  • Volatile是輕量級(jí)的synchronized,因?yàn)樗粫?huì)引起上下文的切換和調(diào)度,所以Volatile性能更好。
  • Volatile只能修飾變量,synchronized可以修飾方法,靜態(tài)方法,代碼塊。
  • Volatile對(duì)任意單個(gè)變量的讀/寫具有原子性,但是類似于i++這種復(fù)合操作不具有原子性。而鎖的互斥執(zhí)行的特性可以確保對(duì)整個(gè)臨界區(qū)代碼執(zhí)行具有原子性。
  • 多線程訪問volatile不會(huì)發(fā)生阻塞,而synchronized會(huì)發(fā)生阻塞。
  • volatile是變量在多線程之間的可見性,synchronize是多線程之間訪問資源的同步性。

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

相關(guān)文章

  • Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析

    Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析

    這篇文章主要介紹了Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析,文章通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • IDEA 2020.3.X 創(chuàng)建scala環(huán)境的詳細(xì)教程

    IDEA 2020.3.X 創(chuàng)建scala環(huán)境的詳細(xì)教程

    這篇文章主要介紹了IDEA 2020.3.X 創(chuàng)建scala環(huán)境的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • java使用MulticastSocket實(shí)現(xiàn)基于廣播的多人聊天室

    java使用MulticastSocket實(shí)現(xiàn)基于廣播的多人聊天室

    這篇文章主要為大家詳細(xì)介紹了java使用MulticastSocket實(shí)現(xiàn)基于廣播的多人聊天室,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 使用Spring Boot創(chuàng)建Web應(yīng)用程序的示例代碼

    使用Spring Boot創(chuàng)建Web應(yīng)用程序的示例代碼

    本篇文章主要介紹了使用Spring Boot創(chuàng)建Web應(yīng)用程序的示例代碼,我們將使用Spring Boot構(gòu)建一個(gè)簡單的Web應(yīng)用程序,并為其添加一些有用的服務(wù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • Java多線程中Lock鎖的使用總結(jié)

    Java多線程中Lock鎖的使用總結(jié)

    這篇文章主要介紹了Java多線程中Lock鎖的使用總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Java?Stream?API詳解與使用示例詳解

    Java?Stream?API詳解與使用示例詳解

    Java?Stream?API?是一個(gè)功能強(qiáng)大的工具,適用于處理集合和數(shù)據(jù)流,本文全面介紹了?Java?Stream?API?的概念、功能以及如何在?Java?中有效地使用它進(jìn)行集合和數(shù)據(jù)流的處理,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • 詳解Java攔截器以及自定義注解的使用

    詳解Java攔截器以及自定義注解的使用

    這篇文章主要為大家介紹了Java攔截器以及自定義注解的使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助<BR>
    2021-12-12
  • springboot中spring.profiles.include的妙用分享

    springboot中spring.profiles.include的妙用分享

    這篇文章主要介紹了springboot中spring.profiles.include的妙用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 解決多模塊項(xiàng)目中Mybatis的Mapper內(nèi)部方法找不到的問題

    解決多模塊項(xiàng)目中Mybatis的Mapper內(nèi)部方法找不到的問題

    這篇文章主要介紹了解決多模塊項(xiàng)目中Mybatis的Mapper內(nèi)部方法找不到的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java?新特性之Option示例詳解

    Java?新特性之Option示例詳解

    使用Optional開發(fā)時(shí)要注意正確使用Optional的“姿勢(shì)”,特別注意不要使用3.2節(jié)提到的錯(cuò)誤示范,謹(jǐn)慎使用isPresent()和get()方法,盡量多使用map()、filter()、orElse()等方法來發(fā)揮Optional的作用,對(duì)Java??Option相關(guān)知識(shí)感興趣的朋友一起看看吧
    2024-02-02

最新評(píng)論