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

深入解析Java中volatile的底層原理

 更新時(shí)間:2023年07月11日 11:14:23   作者:賢子磊  
這篇文章主要介紹了深入解析Java中volatile的底層原理,volatile關(guān)鍵字用于保證變量的可見(jiàn)性和禁止指令重排序,即當(dāng)一個(gè)線程修改了volatile變量的值,其他線程能夠立即看到這個(gè)變量的最新值,而不是使用緩存中的舊值,需要的朋友可以參考下

一、前言

之前我們學(xué)習(xí)過(guò)synchronized,知道它是一個(gè)重量級(jí)的鎖,雖然jdk1.6對(duì)其做了很大的優(yōu)化,但是成本還是較高。因此Java另一個(gè)關(guān)鍵字閃亮登場(chǎng)——volatile。volatile又被稱為輕量級(jí)的synchronized,它在多處理器中保證了共享變量的可見(jiàn)性。volatile變量修飾符如果使用恰當(dāng)?shù)脑?,它比synchronized的使用和執(zhí)行成本會(huì)更低。下面我們將深入剖析volatile的實(shí)現(xiàn)原理。

二、什么是volatile

Java語(yǔ)言規(guī)范第3版中對(duì)volatile的定義如下

Java編程語(yǔ)言允許線程訪問(wèn)共享變量,為了確保共享變量能被準(zhǔn)確和一致地更新,線程應(yīng)該確保通過(guò)排他鎖單獨(dú)獲得這個(gè)變量。Java語(yǔ)言提供了volatile,在某些情況下比鎖要更加方便。如果一個(gè)字段被聲明成volatile,Java線程內(nèi)存模型確保所有線程看到這個(gè)變量的值是一致的。

三、volatile的語(yǔ)義

當(dāng)一個(gè)共享變量被volatile修飾之后,這個(gè)變量就具備了兩層語(yǔ)義:

  • 保證共享變量的可見(jiàn)性:保證了不同線程對(duì)這個(gè)變量進(jìn)行操作時(shí)的可見(jiàn)性,即一個(gè)線程修改了某個(gè)變量的值,這新值對(duì)其他線程來(lái)說(shuō)是立即可見(jiàn)的。
  • 防止局部指令重排序:happens-before規(guī)則中的volatile變量規(guī)則規(guī)定了一個(gè)線程先去寫一個(gè)volatile變量,然后一個(gè)線程去讀這個(gè)變量,那么這個(gè)寫操作的結(jié)果一定對(duì)讀的這個(gè)線程可見(jiàn)。

四、volatile的使用

1. volatile保證共享變量的可見(jiàn)性

public class MyTest {
    public static boolean stop = false;
    public static void main(String[] args) throws InterruptedException {
        //線程1
        Thread thread1 = new Thread(() -> {
            while (!stop) {
                //do something...
            }
        });
        //線程2
        Thread thread2 = new Thread(() -> doStop());
        thread1.start();
        //這里的睡眠是保證線程1先執(zhí)行
        Thread.sleep(1000);
        thread2.start();
    }
    public static void doStop() {
        stop = true;
    }
}

如上所示的代碼,主要用于線程2去利用共享變量stop去中斷線程1的執(zhí)行。正常情況下如果線程2先執(zhí)行,線程1再執(zhí)行不會(huì)有問(wèn)題,線程2能夠正常結(jié)束線程1的執(zhí)行,但是上述的代碼會(huì)一直死循環(huán)無(wú)法結(jié)束。

這是因?yàn)榫€程1先執(zhí)行,此時(shí)stop的值初始為false,線程1執(zhí)行死循環(huán)。根據(jù)Java內(nèi)存模型,每個(gè)線程不能直接訪問(wèn)主內(nèi)存的,需另拷貝一份到自己的工作內(nèi)存中。因此線程1執(zhí)行的時(shí)候會(huì)拷貝stop的副本到自己的工作空間內(nèi),而線程2執(zhí)行的時(shí)候雖然修改了stop的值,但是線程1感知不到,因此會(huì)一直死循環(huán)

因此我們可以使用volatile關(guān)鍵字修改stop變量

public static volatile boolean stop =false;

這里的volatile有兩個(gè)作用

  • 被volatile關(guān)鍵字修飾的變量修改時(shí)會(huì)立即寫入主內(nèi)存
  • 被volatile關(guān)鍵字修飾的變量修改時(shí),其余線程存放的該變量的副本會(huì)被置為無(wú)效

因此當(dāng)線程2修改stop的值的時(shí)候,會(huì)立即寫回主內(nèi)存,同時(shí)使線程1的工作內(nèi)存中的stop變量置為無(wú)效。線程1在每次循環(huán)讀取stop的時(shí)候發(fā)現(xiàn)自己的stop變量無(wú)效了,會(huì)重新去主內(nèi)存讀取最新的stop的值,即讀取到線程2修改過(guò)的最新值,程序正常結(jié)束。

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

//線程1:
context = loadContext();   //語(yǔ)句1
inited = true;             //語(yǔ)句2
//線程2:
while(!inited ){
    sleep()
}
doSomethingwithconfig(context);

借用網(wǎng)上的一個(gè)經(jīng)典案例,線程1負(fù)責(zé)初始化上下文,線程2則是線程1初始化結(jié)束后進(jìn)行的后續(xù)操作。我們知道處理器為了提高程序運(yùn)行效率,可能會(huì)對(duì)輸入代碼進(jìn)行重排優(yōu)化,其中語(yǔ)句1和語(yǔ)句2不存在依賴關(guān)系,因此處理器可能會(huì)將語(yǔ)句1和語(yǔ)句2進(jìn)行調(diào)換順序,即先執(zhí)行語(yǔ)句2再執(zhí)行語(yǔ)句1,那么如果線程1先執(zhí)行完語(yǔ)句2,還沒(méi)有來(lái)得及執(zhí)行語(yǔ)句1的時(shí)候,線程開始執(zhí)行,發(fā)現(xiàn)inited已經(jīng)為true,它就會(huì)認(rèn)為上下文已經(jīng)被初始化完成了,從而會(huì)調(diào)用doSomethingwithconfig方法,此時(shí)就會(huì)發(fā)生錯(cuò)誤。因此我們可以使用volatile關(guān)鍵字修改inited變量,這樣會(huì)禁止inited語(yǔ)句前后的重排序,從而保證了線程安全性。

五、volatile的實(shí)現(xiàn)機(jī)制和原理

通過(guò)前面對(duì)volatile的語(yǔ)義的使用的介紹,相信大家已經(jīng)有了一個(gè)初步的了解,但是volatile為什么會(huì)實(shí)現(xiàn)這些特性呢?接來(lái)下我們就正式進(jìn)入volatile底層原理的講解。

1. 內(nèi)存屏障

在介紹原理前,我們先了解一下內(nèi)存屏障。

1.1 什么是內(nèi)存屏障

  • 內(nèi)存屏障(memory barrier)是一個(gè)CPU指令。這條指令可以確保一些特定指令的執(zhí)行順序,影響一些數(shù)據(jù)的可見(jiàn)性(可能是某些指令執(zhí)行后的結(jié)果)。
  • 插入一個(gè)內(nèi)存屏障,相當(dāng)于告訴CPU和編譯器先于這個(gè)命令的必須先執(zhí)行,后于這個(gè)命令的必須后執(zhí)行。內(nèi)存屏障另一個(gè)作用是強(qiáng)制更新一次不同CPU的緩存。

1.2 內(nèi)存屏障的分類

  • LoadLoad屏障
Load1; 
LoadLoad屏障; 
Load2;

Load1Load2 代表兩條讀取指令。在Load2要讀取的數(shù)據(jù)被訪問(wèn)前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。

  • StoreStore屏障
Store1;
StoreStore屏障;
Store2;

Store1Store2代表兩條寫入指令。在Store2寫入執(zhí)行前,保證Store1的寫入操作對(duì)其它處理器可見(jiàn)

  • LoadStore屏障
Load1;
LoadStore屏障;
Store2;

在Store2被寫入前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。

  • StoreLoad屏障
Store1;
StoreLoad屏障;
Load2;

在Load2讀取操作執(zhí)行前,保證Store1的寫入對(duì)所有處理器可見(jiàn)。StoreLoad屏障的開銷是四種屏障中最大的。

2. 實(shí)現(xiàn)機(jī)制

從代碼的層面我們看不到volatile的實(shí)現(xiàn)機(jī)制,因此我們需要從匯編指令的層次進(jìn)行研究,我們查看一下第四章第一節(jié)代碼中doStop方法的匯編指令

# 可以看到此時(shí)有一個(gè)lock前綴指令
0x0000000003226f6e: lock add dword ptr [rsp],0h  ;*putstatic stop
                                                ; - com.jicl.MyTest::doStop@1 (line 35)

lock前綴指令實(shí)際上相當(dāng)于一個(gè)內(nèi)存屏障(也成內(nèi)存柵欄),它會(huì)提供3個(gè)功能

確保指令重排序時(shí)不會(huì)把其后面的指令排到內(nèi)存屏障之前的位置,也不會(huì)把前面的指令排到內(nèi)存屏障的后面;即在執(zhí)行到內(nèi)存屏障這句指令時(shí),在它前面的操作已經(jīng)全部完成;

強(qiáng)制將對(duì)緩存的修改操作立即寫入主存,利用緩存一致性機(jī)制,并且緩存一致性機(jī)制會(huì)阻止同時(shí)修改由兩個(gè)以上CPU緩存的內(nèi)存區(qū)域數(shù)據(jù)。

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

volatile的底層實(shí)現(xiàn)是通過(guò)插入內(nèi)存屏障,但是對(duì)于編譯器來(lái)說(shuō),發(fā)現(xiàn)一個(gè)最優(yōu)布置來(lái)最小化插入內(nèi)存屏障的總數(shù)幾乎是不可能的,所以,JMM采用了保守策略。如下:

  • 在每一個(gè)volatile寫操作前面插入一個(gè)StoreStore屏障:保證在volatile寫之前,其前面的所有普通寫操作都已經(jīng)刷新到主內(nèi)存中
  • 在每一個(gè)volatile寫操作后面插入一個(gè)StoreLoad屏障:避免volatile寫與后面可能有的volatile讀/寫操作重排序
  • 在每一個(gè)volatile讀操作后面插入一個(gè)LoadLoad屏障:禁止處理器把上面的volatile讀與下面的普通讀重排序
  • 在每一個(gè)volatile讀操作后面插入一個(gè)LoadStore屏障:禁止處理器把上面的volatile讀與下面的普通寫重排序

3. 實(shí)現(xiàn)原理

在介紹完volatile的底層實(shí)現(xiàn)機(jī)制,我們來(lái)分析volatile是如何實(shí)現(xiàn)可見(jiàn)性和有序性的

3.1 可見(jiàn)性

如果對(duì)聲明了volatile變量進(jìn)行寫操作時(shí),JVM會(huì)向處理器發(fā)送一條Lock前綴的指令,將這個(gè)變量所在緩存行的數(shù)據(jù)寫會(huì)到系統(tǒng)內(nèi)存。這一步確保了如果有其他線程對(duì)聲明了volatile變量進(jìn)行修改,則立即更新主內(nèi)存中數(shù)據(jù)。但這時(shí)候其他處理器的緩存還是舊的,所以在多處理器環(huán)境下,為了保證各個(gè)處理器緩存一致,每個(gè)處理會(huì)通過(guò)嗅探在總線上傳播的數(shù)據(jù)來(lái)檢查自己的緩存是否過(guò)期,當(dāng)處理器發(fā)現(xiàn)自己緩存行對(duì)應(yīng)的內(nèi)存地址被修改了,就會(huì)將當(dāng)前處理器的緩存行設(shè)置成無(wú)效狀態(tài),當(dāng)處理器要對(duì)這個(gè)數(shù)據(jù)進(jìn)行修改操作時(shí),會(huì)強(qiáng)制重新從系統(tǒng)內(nèi)存把數(shù)據(jù)讀到處理器緩存里。 這一步確保了其他線程獲得的聲明了volatile變量都是從主內(nèi)存中獲取最新的。

3.2 有序性

Lock前綴指令實(shí)際上相當(dāng)于一個(gè)內(nèi)存屏障(也成內(nèi)存柵欄),它確保指令重排序時(shí)不會(huì)把其后面的指令排到內(nèi)存屏障之前的位置,也不會(huì)把前面的指令排到內(nèi)存屏障的后面;即在執(zhí)行到內(nèi)存屏障這句指令時(shí),在它前面的操作已經(jīng)全部完成

到此這篇關(guān)于深入解析Java中volatile的底層原理的文章就介紹到這了,更多相關(guān)volatile的底層原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析Java和Scala中的Future

    淺析Java和Scala中的Future

    這篇文章主要介紹了Java和Scala中的Future的相關(guān)資料,需要的朋友可以參考下
    2017-10-10
  • 淺析SpringBoot中過(guò)濾器配置與應(yīng)用場(chǎng)景

    淺析SpringBoot中過(guò)濾器配置與應(yīng)用場(chǎng)景

    過(guò)濾器是 Web 三大組件之一,也是項(xiàng)目中常用的工具,這篇文章主要介紹一下過(guò)濾器的概念及在 Spring Boot 中的常用使用方法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-05-05
  • 一文了解Java中枚舉的使用

    一文了解Java中枚舉的使用

    Java中枚舉,大家在項(xiàng)目中經(jīng)常使用吧,主要用來(lái)定義一些固定值。那你了解枚舉的本質(zhì)嗎?了解枚舉的一些常見(jiàn)用法嗎?本文就來(lái)為大家一一進(jìn)行詳解
    2022-09-09
  • SpringBoot集成MyBatis中SQL攔截器的實(shí)戰(zhàn)指南

    SpringBoot集成MyBatis中SQL攔截器的實(shí)戰(zhàn)指南

    這篇文章主要為大家詳細(xì)介紹了SpringBoot集成MyBatis中SQL攔截器的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-07-07
  • SpringBoot生成PDF的五種實(shí)現(xiàn)方法總結(jié)

    SpringBoot生成PDF的五種實(shí)現(xiàn)方法總結(jié)

    這篇文章主要介紹了SpringBoot生成PDF的五種實(shí)現(xiàn)方法,在開發(fā)中經(jīng)常會(huì)遇到需要進(jìn)行對(duì)一些數(shù)據(jù)進(jìn)行動(dòng)態(tài)導(dǎo)出PDF文件,然后讓用戶自己選擇是否需要打印出來(lái),這篇文章我們來(lái)介紹五種實(shí)現(xiàn)方法,需要的朋友可以參考下
    2024-10-10
  • Springmvc中的轉(zhuǎn)發(fā)重定向和攔截器的示例

    Springmvc中的轉(zhuǎn)發(fā)重定向和攔截器的示例

    本篇文章主要介紹了Springmvc中的轉(zhuǎn)發(fā)重定向和攔截器的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • 關(guān)于@Autowired的使用及注意事項(xiàng)

    關(guān)于@Autowired的使用及注意事項(xiàng)

    這篇文章主要介紹了關(guān)于@Autowired的使用及注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 深入理解Java序列化與反序列化

    深入理解Java序列化與反序列化

    今天教大家深入理解Java的序列化與反序列化,文中介紹的非常詳細(xì),有很多代碼示例,對(duì)正在學(xué)習(xí)Java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • Java的Spring框架中實(shí)現(xiàn)發(fā)送郵件功能的核心代碼示例

    Java的Spring框架中實(shí)現(xiàn)發(fā)送郵件功能的核心代碼示例

    這篇文章主要介紹了Java的Spring框架中實(shí)現(xiàn)發(fā)送郵件功能的核心代碼示例,包括發(fā)送帶附件的郵件功能的實(shí)現(xiàn),需要的朋友可以參考下
    2016-03-03
  • java?JIT調(diào)優(yōu)的實(shí)現(xiàn)

    java?JIT調(diào)優(yōu)的實(shí)現(xiàn)

    JIT編譯器調(diào)優(yōu)方法包括啟用JIT日志、優(yōu)化熱點(diǎn)代碼、循環(huán)展開、內(nèi)聯(lián)優(yōu)化、逃逸分析以及使用性能分析工具等,本文主要介紹了java?JIT調(diào)優(yōu)的實(shí)現(xiàn),感興趣的可以了解一下
    2025-02-02

最新評(píng)論

宜丰县| 绩溪县| 孝昌县| 上饶市| 凌源市| 平邑县| 大兴区| 新乐市| 赣州市| 福清市| 兴化市| 宝丰县| 丽水市| 连州市| 巨野县| 滨海县| 夏邑县| 阳江市| 潜山县| 油尖旺区| 巨鹿县| 肥东县| 竹北市| 余庆县| 施甸县| 利辛县| 加查县| 从江县| 呼玛县| 东港市| 潢川县| 正安县| 石家庄市| 桑植县| 高清| 汨罗市| 台南县| 依兰县| 凤凰县| 定日县| 南和县|