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

Volatile關鍵字的使用案例

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

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

一、線程可見性

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

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() + "結束");
        }, "Thread1").start();
        new Thread(() -> {
            try {
                Thread.sleep(2000);
                test.updateFlag();
            } catch (InterruptedException e) {
            }
        }, "Thread2").start();
    }
}

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

修改flag值為:false

我們把flag 變量加上volatile:

volatile  boolean flag = true;

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

修改flag值為:false

Thread1結束

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

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

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

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

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

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

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

二、順序一致性

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

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

Volatile寫插入內存屏障后生成指令序列示意圖:

Volatile讀插入內存屏障后生成指令序列示意圖:

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

防止重排序使用案例:

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)建一個對象主要分為如下三步:

  • 分配對象的內存空間。
  • 初始化對象。
  • 設置instance指向內存空間。

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

二、Volatile與Synchronized比較

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

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

相關文章

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

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

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

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

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

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

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

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

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

    Java多線程中Lock鎖的使用總結

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

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

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

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

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

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

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

    解決多模塊項目中Mybatis的Mapper內部方法找不到的問題

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

    Java?新特性之Option示例詳解

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

最新評論