色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

介紹下volatile的底層原理

冬至子 ? 來源:并發(fā)編程之美 ? 作者:妙啊 ? 2023-06-09 16:17 ? 次閱讀

介紹

線程安全的三大特性,原子性、可見性、有序性,這三大特性與我們之前整理的內(nèi)容息息相關(guān)。本篇重點(diǎn)介紹下volatile的底層原理,幫助我們更好的理解java并發(fā)包。

一、原子性

提供了互斥訪問,同一時(shí)刻只能有一個(gè)線程來對(duì)它進(jìn)行操作。

1. 原子性-synchronizes

2. 原子性-lock

  • lock屬于jdk提供的代碼層面上的鎖,后面單獨(dú)總結(jié)。

3. 原子性-cas

4. 原子性-對(duì)比

  • synchronized:不可中斷鎖(在作用范圍內(nèi)必須等待執(zhí)行完),適合競(jìng)爭(zhēng)不激烈。
  • Lock:可中斷鎖(unlock),競(jìng)爭(zhēng)激烈時(shí)能保持性能常態(tài)。
  • Atomic:競(jìng)爭(zhēng)激烈時(shí)能保持性能常態(tài),比Lock性能好,只能同步一個(gè)值。

二、可見性

可見性指的是一個(gè)線程對(duì)主內(nèi)存的修改,可以被其他線程及時(shí)的觀察到。導(dǎo)致共享變量在線程間不可見的原因:

  • 線程交叉執(zhí)行。
  • 重排序結(jié)合線程交叉執(zhí)行。
  • 共享變量更新后的值沒有在工作內(nèi)存與主內(nèi)存間及時(shí)更新。

1. 可見性-synchronizes

JMM中關(guān)于synchronized的內(nèi)存語(yǔ)意:

  • 進(jìn)入synchronized塊的內(nèi)存語(yǔ)義是把synchronized塊內(nèi)使用到的變量從線程的工作內(nèi)存中清除,這樣synchronized塊內(nèi)使用到該變量就是直接從主內(nèi)存中獲取。
  • 退出synchronized塊的內(nèi)存語(yǔ)義是把synchronized塊內(nèi)對(duì)共享變量的修改刷新到主內(nèi)存。

2. 可見性-volatile

通過加入內(nèi)存屏障禁止重排序優(yōu)化來實(shí)現(xiàn)。

JMM中關(guān)于volatile的內(nèi)存語(yǔ)意:

  • 當(dāng)線程寫入volatile變量值時(shí)就等價(jià)于線程退出synchronized同步塊(對(duì)volatile變量寫操作時(shí),會(huì)在寫操作后加入一條store屏障指令,將本地內(nèi)存中的共享變量值刷新到主內(nèi)存)。
  • 讀取volatile變量值時(shí)就相當(dāng)于進(jìn)入到同步塊(對(duì)volatile變量讀操作,會(huì)在讀操作前加入一條load屏障指令,從主內(nèi)存中讀取共享變量)。

當(dāng)一個(gè)變量被聲明為volatile時(shí),線程在寫入變量時(shí)不會(huì)把值緩存在寄存器中,而是會(huì)把值刷新回主內(nèi)存。當(dāng)其他線程讀取該共享變量時(shí),會(huì)從主內(nèi)存重新獲取最新值。

下面看一個(gè)volatile內(nèi)存可見性的例子:

public class VolatileCanSeeTest {


    private static volatile boolean initFlag = false;


    public static void main(String[] args) throws InterruptedException {
        new Thread(() - > {
            log.info("init begin");
            while(!initFlag) {
            }


            // if(!initFlag) {while(true){}} // JIT
            log.info("===success===");
        }).start();


        Thread.sleep(1000);


        new Thread(() - > doSomething()).start();
    }


    public static void doSomething() {
        log.info("doSomething begin");
        initFlag = true;
        log.info("doSomething end");
    }
}

查看對(duì)應(yīng)的匯編代碼,可以看到使用volatile匯編指令會(huì)加上lock前綴指令:

圖片

  • rsp】是寄存器的意思,在java內(nèi)存模型一節(jié)中我們介紹了工作內(nèi)存就是寄存器以及cpu告訴緩存等的一個(gè)抽象概念。
  • 這里值得一提的是,重排序只是編譯器優(yōu)化的一種表現(xiàn),上面這段代碼主要是編譯器優(yōu)化導(dǎo)致的。編譯器會(huì)認(rèn)為這段循環(huán)代碼在單線程運(yùn)行中,initFlag變量不會(huì)被改變,從而優(yōu)化為:
if(!initFlag) {
    while(true){
    }
}

這里結(jié)合java內(nèi)存模型對(duì)volatile底層原理進(jìn)行說明:

圖片

  • 這里添加lock前綴指令的意思是當(dāng)cpu執(zhí)行引擎處理完共享變量的計(jì)算后,通過asign指令將共享變量回寫到工作內(nèi)存中后會(huì)立即將該共享變量通過store和write指令回寫到主內(nèi)存,并且給這兩個(gè)cpu指令加lock指令鎖。
  • 同時(shí),結(jié)合cpu緩存一致性協(xié)議,當(dāng)共享變量回寫主內(nèi)存時(shí),經(jīng)過總線觸發(fā)MESI協(xié)議,另其他包含了該共享變量的緩存行置為無(wú)效狀態(tài),所以其他線程需要從主內(nèi)存中重新加載該共享變量到自己的工作內(nèi)存,從而保證了共享變量的內(nèi)存可見性。同時(shí),結(jié)合[cpu緩存一致性協(xié)議,當(dāng)共享變量回寫主內(nèi)存時(shí),經(jīng)過總線觸發(fā)MESI協(xié)議,另其他包含了該共享變量的緩存行置為無(wú)效狀態(tài),所以其他線程需要從主內(nèi)存中重新加載該共享變量到自己的工作內(nèi)存,從而保證了共享變量的內(nèi)存可見性。

3. 可見性-對(duì)比

  • synchronized:保證可見性和原子性,但可能會(huì)導(dǎo)致線程上下文切換和增加重新調(diào)度的開銷。
  • volatile:只能保證共享變量的可見性,不能解決讀-改-寫等的原子性問題。
  • 關(guān)于共享內(nèi)存可見性以及JMM詳見:java內(nèi)存模型

三、有序性

一個(gè)線程觀察其他線程中的指令執(zhí)行順序,由于指令重排序的存在,該觀察結(jié)果一般雜亂無(wú)序。

1. 有序性-happens-before原則

java內(nèi)存模型中允許編譯器和處理器對(duì)指令進(jìn)行重排序,但是重排序過程不會(huì)影響到單線程程序的執(zhí)行,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性??梢酝ㄟ^volatile關(guān)鍵字來保證一定的有序性,可以通過synchronized、lock保證同一時(shí)刻線程順序執(zhí)行來保證有序性。另外,java內(nèi)存模型具備先天的有序性,稱為**happens-before **原則:

  • 程序次序規(guī)則(保證單線程的有序性,不保證多線程的有序性)

    一個(gè)線程內(nèi),按照代碼順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作。

  • 鎖定規(guī)則

    一個(gè)unlock操作先行發(fā)生于對(duì)后面同一個(gè)鎖的lock操作。

  • volatile變量規(guī)則

    對(duì)一個(gè)變量的寫操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作。

  • 傳遞規(guī)則

    如果操作A先行發(fā)生于操作B,而操作B又先行發(fā)生于操作C,則可以得出操作A先行發(fā)生于操作C。

  • 線程啟動(dòng)規(guī)則

    Thread對(duì)象的start()方法先行發(fā)生于此線程的每一個(gè)動(dòng)作。

  • 線程中斷規(guī)則

    對(duì)線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到中斷事件的發(fā)生。

  • 線程終結(jié)規(guī)則

    線程中所有的操作都先行發(fā)生于線程的終止檢測(cè),可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值手段檢測(cè)到線程已經(jīng)終止執(zhí)行。

  • 對(duì)象終結(jié)規(guī)則

    一個(gè)對(duì)象的初始化完成先行發(fā)生于他的finalize()方法的開始。

如果兩個(gè)操作的執(zhí)行順序無(wú)法從happens-before原則推導(dǎo)出來,那么就無(wú)法保證他們的有序性,虛擬機(jī)可以隨意的對(duì)他們重排序。

2. 有序性-synchronizes

首先,可以明確的一點(diǎn)是:synchronized是無(wú)法禁止指令重排和處理器優(yōu)化的。那么他是如何保證的有序性呢?

synchronized保證的有序性是多個(gè)線程之間的有序性,即被加鎖的內(nèi)容要按照順序被多個(gè)線程執(zhí)行。但是其內(nèi)部的同步代碼還是會(huì)發(fā)生重排序,只不過由于編譯器和處理器都遵循as-if-serial語(yǔ)義,所以我們可以認(rèn)為這些重排序在單線程內(nèi)部可忽略。

as-if-serial語(yǔ)義的意思指:不管怎么重排序,單線程程序的執(zhí)行結(jié)果都不能被改變。編譯器和處理器無(wú)論如何優(yōu)化,都必須遵守as-if-serial語(yǔ)義。簡(jiǎn)單說就是,as-if-serial語(yǔ)義保證了單線程中,不管指令怎么重排,最終的執(zhí)行結(jié)果是不能被改變的。

3. 有序性-volatile

java內(nèi)存模型允許編譯器和處理器對(duì)指令重排序提高運(yùn)行性能,并且只會(huì)對(duì)不存在數(shù)據(jù)依賴性的指令重排序。例:

int a = 1;
int b = 2;
int c = a + b;

變量c的值依賴a和b的值,所以重排序后能保證c的操作在a,b之后,但是a,b誰(shuí)先執(zhí)行就不一定,這在單線程下不存在問題。下面看一個(gè)多線程下指令重排序的例子:

public class VolatileSerialTest {


    private static int x = 0, y = 0;


    public static void main(String[] args) throws InterruptedException{
        Set< String > resultSet = new HashSet<  >();
        Map< String, Integer > resultMap = new HashMap<  >();


        for (int i = 0; i < 1000000; i++) {
            x = 0;
            y = 0;
            resultMap.clear();
            Thread one = new Thread(() - > {
                int a = y;
                x = 1;
                resultMap.put("a", a);
            });


            Thread two = new Thread(() - > {
                int b = x;
                y = 1;
                resultMap.put("b", b);
            });


            one.start();
            two.start();
            one.join();
            two.join();


            resultSet.add("a=" + resultMap.get("a") + "," + "b=" + resultMap.get("b"));
            log.info("ab結(jié)果:{}", resultSet);
        }
    }
}

由于指令重排序?qū)е驴赡艹霈F(xiàn)的結(jié)果有:

圖片

volatile禁止指令重排序原理:

圖片

volatile通過加入內(nèi)存屏障禁止指令重排序。 編譯器會(huì)根據(jù)volatile/synchronized/final等的語(yǔ)義,在特定的位置插入內(nèi)存屏障。 當(dāng)遇到特定的內(nèi)存屏障指令時(shí),處理器將禁止其對(duì)應(yīng)的重排序,保證屏障前面的操作可以被后面的操作可見。

4. 有序性-對(duì)比

  • synchronized是一種鎖機(jī)制,存在阻塞問題和性能問題,而volatile并不是鎖,所以不存在阻塞和性能問題。
  • volatile借助了內(nèi)存屏障來幫助其解決可見性和有序性問題,而內(nèi)存屏障的使用還為其帶來了一個(gè)禁止指令重排的附件功能,所以在有些場(chǎng)景中是可以避免發(fā)生指令重排的問題的。

結(jié)語(yǔ)

本文總結(jié)了線程安全的三大特性,同時(shí)文中幾乎涉及到了所以之前總結(jié)過的知識(shí),在閱讀過程中可以參考之前的文章進(jìn)行理解。至此,我們對(duì)并發(fā)包基礎(chǔ)應(yīng)該有了完整的認(rèn)識(shí)。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5357

    瀏覽量

    120683
  • JAVA語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    20114
  • volatile
    +關(guān)注

    關(guān)注

    0

    文章

    45

    瀏覽量

    13039
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    volatile 和 const

    volatile; 2、多任務(wù)環(huán)境各任務(wù)間共享的標(biāo)志應(yīng)該加volatile; 3、存儲(chǔ)器映射的硬件寄存器通常也要加volatile說明,因?yàn)槊看螌?duì)它的讀寫都可能由不同意義;我認(rèn)為這是
    發(fā)表于 06-23 23:20

    介紹計(jì)算機(jī)底層知識(shí)

    的更遠(yuǎn),而計(jì)算機(jī)基礎(chǔ)知識(shí)又是重中之重。下面,跟隨我的腳步,為你介紹計(jì)算機(jī)底層知識(shí)。CPU還不了解 CPU 嗎?現(xiàn)在就帶你了解一 CPU 是什么CPU 的全稱是Central Pr
    發(fā)表于 07-26 06:21

    介紹計(jì)算機(jī)底層知識(shí)

    我們每個(gè)程序員或許都有一個(gè)夢(mèng),那就是成為大牛,我們或許都沉浸在各種框架中,以為框架就是一切,以為應(yīng)用層才是最重要的,你錯(cuò)了。在當(dāng)今計(jì)算機(jī)行業(yè)中,會(huì)應(yīng)用是基本素質(zhì),如果你懂其原理才能讓你在行業(yè)中走的更遠(yuǎn),而計(jì)算機(jī)基礎(chǔ)知識(shí)又是重中之重。下面,跟隨我的腳步,為你介紹計(jì)算機(jī)
    發(fā)表于 07-28 06:15

    什么是volatile

    volatile06. 附錄01. volatile概述volatile是C語(yǔ)言中的一個(gè)關(guān)鍵字。將變量定義為volatile就表示告訴編譯器這個(gè)變量可能會(huì)被竟想不到地改變,在這種情況
    發(fā)表于 10-28 09:23

    請(qǐng)問一volatile的作用是什么

    請(qǐng)問一volatile的作用是什么?volatile變量有哪些例子呢?
    發(fā)表于 11-11 07:49

    AVR-GCC中如何使用volatile關(guān)鍵字

    volatile的字面含義是易變的,那么將一個(gè)變量指示為volatile是什么意思呢?是告訴編譯器這個(gè)變量是易變的?事實(shí)上也是如此。在多任務(wù)、中斷等環(huán)境,變量可能被其他的任務(wù)改變
    發(fā)表于 07-02 17:11 ?40次下載

    volatile修飾的變量的認(rèn)識(shí)和理解

     談到volatile,理解原子性和易變性是不同的概念這一點(diǎn)很重要,volatile是輕量級(jí)的鎖,它只具備可見性,但沒有原子特性。如果你將一個(gè)域聲明為volatile,那么只要對(duì)這個(gè)域產(chǎn)生了寫操作
    發(fā)表于 12-01 11:36 ?5739次閱讀
    <b class='flag-5'>volatile</b>修飾的變量的認(rèn)識(shí)和理解

    volatile說到i++的線程安全問題

    中斷服務(wù)程序中修改的供其它程序檢測(cè)的變量需要加volatile;多任務(wù)環(huán)境各任務(wù)間共享的標(biāo)志應(yīng)該加volatile;存儲(chǔ)器映射的硬件寄存器通常也要加volatile說明,因?yàn)槊看螌?duì)它
    發(fā)表于 12-01 12:01 ?2993次閱讀
    從<b class='flag-5'>volatile</b>說到i++的線程安全問題

    volatile變量定義的意義和該用在哪里

    volatile 影響編譯器編譯的結(jié)果,volatile指出 變量是隨時(shí)可能發(fā)生變化的,與volatile變量有關(guān)的運(yùn)算,不要進(jìn)行編譯優(yōu)化,以免出錯(cuò)
    發(fā)表于 03-07 15:29 ?3683次閱讀
    <b class='flag-5'>volatile</b>變量定義的意義和該用在哪里

    C語(yǔ)言類型修飾符Volatile的使用說明

    ,確保本條指令不會(huì)因編譯器的優(yōu)化而省略,且要求每次直接讀值。volatile的變量是說這變量可能會(huì)被意想不到地改變,這樣,編譯器就不會(huì)去假設(shè)這個(gè)變量的值了。 一般說來,volatile用在如下的幾個(gè)地方: 1、中斷服務(wù)程序中修改的供其它程序檢測(cè)的變量需要加
    的頭像 發(fā)表于 09-19 10:54 ?3561次閱讀

    如何使用C++語(yǔ)法中的volatile

    volatile volatile int i = 10; volatile 關(guān)鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統(tǒng)、硬件、其它線程等)更改。所以
    的頭像 發(fā)表于 09-09 09:38 ?1496次閱讀

    C++基礎(chǔ)語(yǔ)法之volatile、assert()和sizeof()

    volatile volatile int i = 10; volatile 關(guān)鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統(tǒng)、硬件、其它線程等)更改。所以
    的頭像 發(fā)表于 09-09 09:48 ?1321次閱讀

    【嵌入式】C語(yǔ)言中volatile關(guān)鍵字

    volatile06. 附錄01. volatile概述volatile是C語(yǔ)言中的一個(gè)關(guān)鍵字。將變量定義為volatile就表示告訴編譯器這個(gè)變量可能會(huì)被竟想不到地改變,在這種情況
    發(fā)表于 10-21 10:21 ?6次下載
    【嵌入式】C語(yǔ)言中<b class='flag-5'>volatile</b>關(guān)鍵字

    TiDB底層存儲(chǔ)結(jié)構(gòu)LSM樹原理介紹

    隨著數(shù)據(jù)量的增大,傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)越來越不能滿足對(duì)于海量數(shù)據(jù)存儲(chǔ)的需求。對(duì)于分布式關(guān)系型數(shù)據(jù)庫(kù),我們了解其底層存儲(chǔ)結(jié)構(gòu)是非常重要的。本文將介紹分布式關(guān)系型數(shù)據(jù)庫(kù) TiDB 所采用的底層
    的頭像 發(fā)表于 01-13 10:00 ?1010次閱讀

    volatile的原理

    今天來了解一面試題:你對(duì) volatile 了解多少。要了解 volatile 關(guān)鍵字,就得從 Java 內(nèi)存模型開始。最后到 volatile 的原理。 一、Java 內(nèi)存模型 (
    的頭像 發(fā)表于 10-10 16:33 ?399次閱讀
    <b class='flag-5'>volatile</b>的原理
    主站蜘蛛池模板: 国产在线精品视亚洲不卡| 一个人看的www视频动漫版| 国产又黄又硬又粗| 国产成人自拍视频在线观看| 99久久99久久精品免费看子| 99久久就热视频精品草| MD传媒MD0021在线观看| 扒开女人下面使劲桶视频| 2020年国产精品午夜福利在线观看| 中文字幕偷乱免费视频在线| 999视频精品全部免费观看| 把内衣脱了把奶露出来| 国产第一页在线视频| 好紧的小嫩嫩17p| 老头狠狠挺进小莹体内视频| 九九免费精品视频| 久久只精品99品免费久| 欧美另类z0z000高清| 思思久久99热只有频精品66| 亚洲成人中文| 亚洲精品国产在线观看| 在线播放国产视频| p影院永久免费| 国产欧美亚洲综合第一页| 久草精品在线| 啪啪激情婷婷久久婷婷色五月| 色多多旧版污污破解版| 亚洲精品无夜久久久久久久久| 佐山爱巨大肥臀在线| 中文字幕一区二区视频| 超碰97av 在线人人操| 国产亚洲精品成人AV久久| 老司机无码精品A| 天美传媒麻豆精品| 午夜家庭影院| 在线亚洲97se| 国产99久9在线| 久久理论片| 日韩1区1区产品乱码芒果榴莲| 日本乱hd高清videos| 亚洲精品婷婷无码成人A片在线|