談?wù)剉olatile關(guān)鍵字

概述

? 在多線程訪問(wèn)共享變量時(shí)傻工,java通過(guò)volatile關(guān)鍵字保證變量的可見性,相比于synchronized關(guān)鍵字咙冗,volatile并不會(huì)引起線程上下文的切換沾歪,因此具有較小的開銷。同時(shí)乞娄,volatile可以禁止指令重排序瞬逊。這篇文章將對(duì)以上兩點(diǎn)作用的,希望可以加深對(duì)一些概念的理解仪或。

volatile保證變量可見性

? 共享變量的可見性,簡(jiǎn)單的說(shuō)就是在一個(gè)線程中修改了變量士骤,在另一個(gè)線程可以讀到修改后的變量范删。在對(duì)這塊實(shí)現(xiàn)原理進(jìn)行介紹之前,我們必須對(duì)java內(nèi)存模型有一定的了解拷肌。

java內(nèi)存模型

? java內(nèi)存模型規(guī)定了所有的共享變量(除局部變量及方法參數(shù))都存儲(chǔ)在主內(nèi)存中到旦,而每個(gè)線程也有自己的工作內(nèi)存,線程的工作內(nèi)存保存了該線程使用到的變量的主內(nèi)存的副本巨缘,線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行添忘,而不能直接讀取主內(nèi)存中的變量,具體如下如所示:


jmm模型.png

主內(nèi)存和工作內(nèi)存間的交互通過(guò)如下8中操作來(lái)完成若锁,可以看出主內(nèi)存變量值到工作內(nèi)存可以通過(guò)read和load完成搁骑,工作內(nèi)存到主內(nèi)存變量值可以通過(guò)store和write完成。

(1) lock(鎖定):作用于主內(nèi)存,把一個(gè)變量標(biāo)識(shí)為一個(gè)線程獨(dú)占的狀態(tài)仲器;

(2) unlock(解鎖):作用于主內(nèi)存的變量煤率,它把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后的變量才可以被其他線程鎖定乏冀。

(3) read(讀取):作用于主內(nèi)存蝶糯,把一個(gè)變量從主內(nèi)存?zhèn)鬏數(shù)焦ぷ鲀?nèi)存;

(4) load(載入):作用于工作內(nèi)存辆沦,把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中;

(5) use(使用):作用于工作內(nèi)存的變量昼捍,它把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個(gè)需要使用到變量的值的字節(jié)碼指令時(shí)將會(huì)執(zhí)行這個(gè)操作肢扯。

(6) assign(賦值):作用于工作內(nèi)存的變量端三,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的字節(jié)碼指令時(shí)執(zhí)行這個(gè)操作鹃彻。

(7) store(存儲(chǔ)):作用于工作內(nèi)存的變量郊闯,它把工作內(nèi)存中一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write操作使用蛛株。

(8) write(寫入):作用于主內(nèi)存的變量团赁,它把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中。

? 對(duì)于非volatile變量讀取變量值會(huì)直接執(zhí)行l(wèi)oad谨履,use操作從工作內(nèi)存中讀取變量欢摄,這種可能會(huì)出現(xiàn)讀取的變量值是失效的,與主內(nèi)存中的不一致笋粟。volatile變量通過(guò)強(qiáng)制要求read怀挠,load,use必須連續(xù)出現(xiàn)保證每次讀取前都從主內(nèi)存中刷新最新的值害捕。通過(guò)要求assign绿淋,store,write必須連續(xù)出現(xiàn)保證了修改的數(shù)據(jù)立刻同步到主內(nèi)存中尝盼,這種就保證了變量的可見性吞滞。

volatile禁止指令重排序

? 指令重排序是指編譯器和處理器為了優(yōu)化程序執(zhí)行順序?qū)Τ绦虻膱?zhí)行語(yǔ)句進(jìn)行優(yōu)化。舉一個(gè)簡(jiǎn)單的創(chuàng)建對(duì)象的語(yǔ)句:

dclProblemSingleton = new DCLProblemSingleton();

這條語(yǔ)句會(huì)被jvm解析為以下三個(gè)操作執(zhí)行:(1) 為對(duì)象分配內(nèi)存盾沫;(2) 為對(duì)象賦值裁赠;(3)將內(nèi)存地址賦值給dclProblemSingleton變量。由于(1)和(3)并不存在依賴關(guān)系赴精,所以指令重排序后語(yǔ)句執(zhí)行的順序可能是(1)(3)(2)佩捞。這個(gè)時(shí)候如果作為共享變量多線程訪問(wèn)就會(huì)存在問(wèn)題。經(jīng)典的DCL雙重檢查鎖實(shí)現(xiàn)的飽漢模式的單例就會(huì)存在這個(gè)問(wèn)題蕾哟,可能存在又有的線程訪問(wèn)到未被初始化的對(duì)象一忱,具體代碼如下:

public class DCLProblemSingleton {
    private int ele = 0;

    private static DCLProblemSingleton dclProblemSingleton;

    private DCLProblemSingleton() {}

    public static DCLProblemSingleton getDclProblemSingleton() {
        //第一次檢測(cè)
        if (dclProblemSingleton == null) {
            synchronized (DCLProblemSingleton.class) {
                //第二次檢測(cè)
                if (dclProblemSingleton == null) {
                    dclProblemSingleton = new DCLProblemSingleton();//可能出現(xiàn)問(wèn)題的地方
                }
            }
        }
        return dclProblemSingleton;
    }
}

正是由于java中的指令重排序莲蜘,DCL方式實(shí)現(xiàn)的指令重排序會(huì)存在問(wèn)題,解決這個(gè)問(wèn)題的方式就是通過(guò)將dclProblemSingleton聲明為volatile來(lái)禁止指令重排序掀潮。

內(nèi)存屏障

JDK1.5中已引入了內(nèi)存屏障菇夸,volatile關(guān)鍵字是通過(guò)內(nèi)存屏障(柵欄)來(lái)實(shí)現(xiàn)的禁止質(zhì)量重排序。內(nèi)存屏障又稱為內(nèi)存柵欄仪吧,是一組cpu指令庄新。不同架構(gòu)的cpu的內(nèi)存屏障實(shí)現(xiàn)都不相同,我們以Intel x86為例子薯鼠,其實(shí)現(xiàn)了如下內(nèi)存柵欄:

(1) sfence: 確保sfence前所有的store先與sfence所有的store命令執(zhí)行择诈;

(2) Ifence: 確保Ifence前所有的load先與Ifence所有的load命令執(zhí)行;

(3) mfence: 功能相當(dāng)于sfence和mfence總和出皇。

(4) Lock前綴羞芍,Lock不是一種內(nèi)存屏障,但是它能完成類似內(nèi)存屏障的功能郊艘。Lock會(huì)對(duì)CPU總線和高速緩存加鎖荷科,可以理解為CPU指令級(jí)的一種鎖。

有了上述的內(nèi)存屏障命令我們看下volatile是如何實(shí)現(xiàn)禁止指令重排序的纱注?

在x86平臺(tái)上volatile是通過(guò)Lock前綴實(shí)現(xiàn)的畏浆,Lock前綴先對(duì)總線/緩存加鎖,然后執(zhí)行后面的指令狞贱,最后釋放鎖后會(huì)把高速緩存中的臟數(shù)據(jù)全部刷新回主內(nèi)存刻获。在Lock前綴鎖住總線/緩存期間,其他cpu的讀寫請(qǐng)求都會(huì)被阻塞直到釋放瞎嬉,Lock后的寫操作會(huì)會(huì)使得其他cpu的的緩存失效蝎毡。例如以上的賦值語(yǔ)句dclProblemSingleton = new DCLProblemSingleton(),如果dclProblemSingleton被volatile關(guān)鍵字修飾氧枣,那么執(zhí)行(3)上加上lock沐兵,那么(3)前面的步驟(1)(2)不執(zhí)行完成就無(wú)法執(zhí)行(3),也就禁止了指令重排序挑胸。在lock釋放后痒筒,又會(huì)將緩存刷新到主存中。

內(nèi)存屏障與上述的可見性實(shí)現(xiàn)是否有關(guān)系茬贵?

在上面的lock操作中,在lock釋放后會(huì)將緩存刷新到主存中移袍,也就是我們上面所將的assign解藻,store,write必須連續(xù)出現(xiàn)葡盗。我理解volatile的可見性在jvm層面是通過(guò)read螟左,load啡浊,use必須連續(xù)出現(xiàn),assign胶背,store巷嚣,write必須連續(xù)出現(xiàn)保證的。而這個(gè)連續(xù)出現(xiàn)則是內(nèi)存屏障使用的表象钳吟,底層原理還是依靠?jī)?nèi)存屏障來(lái)解決廷粒。

原文

袁瓊瓊的技術(shù)博客,歡迎指針
http://yuanqiongqiong.cn/2019/06/09/%E8%B0%88%E8%B0%88volatile%E5%85%B3%E9%94%AE%E5%AD%97/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末红且,一起剝皮案震驚了整個(gè)濱河市坝茎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌暇番,老刑警劉巖嗤放,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異壁酬,居然都是意外死亡次酌,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門舆乔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)岳服,“玉大人,你說(shuō)我怎么就攤上這事蜕煌∨哨澹” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵斜纪,是天一觀的道長(zhǎng)贫母。 經(jīng)常有香客問(wèn)我,道長(zhǎng)盒刚,這世上最難降的妖魔是什么腺劣? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮因块,結(jié)果婚禮上橘原,老公的妹妹穿的比我還像新娘。我一直安慰自己涡上,他們只是感情好趾断,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吩愧,像睡著了一般芋酌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雁佳,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天脐帝,我揣著相機(jī)與錄音同云,去河邊找鬼。 笑死堵腹,一個(gè)胖子當(dāng)著我的面吹牛炸站,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疚顷,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼旱易,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了荡含?” 一聲冷哼從身側(cè)響起咒唆,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎释液,沒想到半個(gè)月后全释,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡误债,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年浸船,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寝蹈。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡李命,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箫老,到底是詐尸還是另有隱情封字,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布耍鬓,位于F島的核電站阔籽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏牲蜀。R本人自食惡果不足惜笆制,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涣达。 院中可真熱鬧在辆,春花似錦、人聲如沸度苔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)寇窑。三九已至奕删,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疗认,已是汗流浹背完残。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留横漏,地道東北人谨设。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像缎浇,于是被迫代替她去往敵國(guó)和親扎拣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Java內(nèi)存區(qū)域 Java虛擬機(jī)在運(yùn)行程序時(shí)會(huì)把其自動(dòng)管理的內(nèi)存劃分為以上幾個(gè)區(qū)域素跺,每個(gè)區(qū)域都有的用途以及創(chuàng)建銷毀...
    架構(gòu)師springboot閱讀 1,772評(píng)論 0 5
  • 第6章類文件結(jié)構(gòu) 6.1 概述 6.2 無(wú)關(guān)性基石 6.3 Class類文件的結(jié)構(gòu) java虛擬機(jī)不和包括java...
    kennethan閱讀 931評(píng)論 0 2
  • 除了充分利用計(jì)算機(jī)處理器的能力外二蓝,一個(gè)服務(wù)端同時(shí)對(duì)多個(gè)客戶端提供服務(wù)則是另一個(gè)更具體的并發(fā)應(yīng)用場(chǎng)景。衡量一個(gè)服務(wù)性...
    胡二囧閱讀 1,340評(píng)論 0 12
  • 緩存一致性問(wèn)題 內(nèi)存模型描述的是程序中各變量(實(shí)例域指厌、靜態(tài)域和數(shù)組元素)之間的關(guān)系刊愚,以及計(jì)算機(jī)系統(tǒng)將變量存...
    tracy_668閱讀 1,043評(píng)論 0 3
  • 1.并發(fā)編程中的三個(gè)概念 在并發(fā)編程中,我們通常會(huì)遇到以下三個(gè)問(wèn)題:原子性問(wèn)題踩验,可見性問(wèn)題鸥诽,有序性問(wèn)題。我們先看具...
    AKyS佐毅閱讀 2,012評(píng)論 1 8