volatile變量

? ? ? ? 上篇文章介紹了Java內(nèi)存模型,沒(méi)看過(guò)《深入理解Java虛擬機(jī)》的同學(xué)可以去看下Java內(nèi)存模型

? ? ? ? Java內(nèi)存模型對(duì)volatile專門定義了一些特殊的訪問(wèn)規(guī)則,假定T表示一個(gè)線程,V和W分別表示兩個(gè)volatile變量,那么在進(jìn)行read、load、use、assign、store、和write操作時(shí)需要滿足如下規(guī)則

1)只有當(dāng)線程T對(duì)變量V執(zhí)行的前一個(gè)動(dòng)作時(shí)load的時(shí)候,線程T才能對(duì)變量V執(zhí)行use動(dòng)作。并且只有線程T對(duì)變量V執(zhí)行的后一個(gè)動(dòng)作是use的時(shí)候,線程T才能對(duì)變量V執(zhí)行l(wèi)oad動(dòng)作。線程T對(duì)變量V的use動(dòng)作可以認(rèn)為是和線程T對(duì)變量V的load、read動(dòng)作相關(guān)聯(lián),必須連續(xù)一起出現(xiàn)(這條規(guī)則要求在工作內(nèi)存中,每次使用V前都必須先從主內(nèi)存刷新最新的值,用于保證能看見其他線程對(duì)變量V所做的修改后的值)

2)只有當(dāng)線程T對(duì)變量V執(zhí)行的前一個(gè)動(dòng)作時(shí)assign的時(shí)候,線程T才能對(duì)變量V執(zhí)行store動(dòng)作,并且,只有當(dāng)線程T對(duì)變量V執(zhí)行的后一個(gè)動(dòng)作是store的時(shí)候,線程T才能對(duì)變量V執(zhí)行assign動(dòng)作。線程T對(duì)變量V的assign動(dòng)作可以認(rèn)為是和線程T對(duì)變量V的store、wtite動(dòng)作相關(guān)聯(lián),必須連續(xù)一起出現(xiàn)(這條規(guī)則要求在工作內(nèi)存中,每次修改V后都必須立刻同步回主內(nèi)存中,用于保證其他線程可以看見自己對(duì)變量V所做的修改)

3)假定動(dòng)作A是線程T對(duì)變量V實(shí)施的use或assign動(dòng)作,動(dòng)作F是和動(dòng)作A相關(guān)聯(lián)的load或store動(dòng)作,假定動(dòng)作P是和動(dòng)作F相對(duì)應(yīng)的對(duì)變量V的read或write動(dòng)作;類似的,假定動(dòng)作B是線程T對(duì)變量W實(shí)施的use或assign動(dòng)作,假定動(dòng)作G是和動(dòng)作B相關(guān)聯(lián)的load或store動(dòng)作,假定動(dòng)作Q是和動(dòng)作G相應(yīng)的對(duì)變量W的read或write動(dòng)作。如果A先于B,那么P先于Q(這條規(guī)則要求volatile修改的變量不會(huì)被指令重排序優(yōu)化,保證代碼的執(zhí)行順序與程序的順序相同)

可以說(shuō)volatile關(guān)鍵字是Java虛擬機(jī)提供的最輕量級(jí)的同步機(jī)制。

? ? ? ? 當(dāng)一個(gè)變量定義為volatile之后具備如下特性:

? ? ? ? 1.可見性:指當(dāng)一條線程修改了這個(gè)變量的值,新值對(duì)于其他線程來(lái)說(shuō)是可以立即得知的。普通變量的值在線程間傳遞均需通過(guò)主內(nèi)存來(lái)完成,例:線程A修改了一個(gè)普通變量的值,然后向主內(nèi)存進(jìn)行回寫,另外一條線程B在線程A回寫完成了后再?gòu)闹鲀?nèi)存進(jìn)行讀取操作,新變量的值才會(huì)對(duì)線程B可見。

? ? ? ? 雖然volatile型變量是線程可見的,對(duì)volatile變量所有的讀寫都能立刻反應(yīng)到其他線程之中,但基于volatile變量的運(yùn)算在并發(fā)下并不是安全的,因?yàn)镴ava里面的運(yùn)算并不是原子操作。例:race ++,我們用javap反編譯這句代碼會(huì)發(fā)現(xiàn)在Class文件中是由4條字節(jié)碼指令構(gòu)成,(這里圖1是書中原圖,圖2是筆者自己反編譯的,可能由于Java虛擬機(jī)版本的問(wèn)題2者不太一樣,請(qǐng)知道的大佬告知,為文章嚴(yán)謹(jǐn)性看圖1就可以了)

圖1
圖2

從字節(jié)碼層面很容易分析出來(lái),假如有兩條線程同時(shí)執(zhí)行這條語(yǔ)句,線程A執(zhí)行g(shù)etstatic指令把race的值取到操作棧頂時(shí),volatile關(guān)鍵字保證來(lái)race的值在此時(shí)是正確的,但是執(zhí)行iconst_1、iadd這些指令時(shí)線程B可能已經(jīng)把race的值加大了,而在操作棧定的值就變成了過(guò)期的數(shù)據(jù)。

? ? ? ? 由于volatile變量只能保證可見性,所以在不符合以下兩條規(guī)則的運(yùn)算場(chǎng)景中,我們?nèi)匀灰ㄟ^(guò)加鎖來(lái)保證原子性

? ? ? ? 1)運(yùn)算結(jié)果并不依賴變量的當(dāng)前值,或者能夠確保只有單一的線程修改變量的值

? ? ? ? 2)變量不需要與其他的狀態(tài)變量共同參與不變約束

2.禁止指令重排序優(yōu)化,這里說(shuō)明下,為了使處理器內(nèi)部的運(yùn)算單元能盡量被充分利用,處理器可能會(huì)對(duì)輸入代碼進(jìn)行亂序執(zhí)行優(yōu)化。與之類似的Java虛擬機(jī)的即時(shí)編譯器也有指令重排序優(yōu)化。還不知道的同學(xué)可以去查下(說(shuō)的就是我自己)。

? ? ? ? 普通的變量?jī)H僅會(huì)保證在該方法的執(zhí)行過(guò)程中所有依賴值結(jié)果的地方都能獲取到正確的結(jié)果,而不能保證變量賦值操作的順序與程序代碼中的執(zhí)行順序一致。這樣理解起來(lái)還是有點(diǎn)抽象,筆者舉個(gè)自己在書中看了茅塞頓開的例子

這里是一段匯編代碼,方便起見就用書中原圖了

這是段標(biāo)準(zhǔn)的DCL單例,變量用valatile修飾,賦值后(mov%eax,0x150(%esi)這句便是賦值操作)多執(zhí)行了一個(gè)“l(fā)ock addl & 0x0,(%esp)”操作,這個(gè)操作相當(dāng)于一個(gè)內(nèi)存屏障,只有一個(gè)cpu訪問(wèn)時(shí)并不需要內(nèi)存屏障,但如果有兩個(gè)或更多cpu訪問(wèn)同一塊內(nèi)存,且其中一個(gè)在觀測(cè)另一個(gè),就需要內(nèi)存屏障來(lái)保證一致性了。這句指令“addl & 0x0,(%esp)”(把esp寄存器的值加0)顯然是一個(gè)空操作,關(guān)鍵在于lock前綴,它的作用是使得本cpu的Cache寫入了內(nèi)存,該寫入動(dòng)作也會(huì)引起別的cpu或者別的內(nèi)核無(wú)效化其Cache,這中操作箱單于對(duì)Cache中的變量做了一個(gè)“store和write”操作。所以通過(guò)這樣一個(gè)空操作可以讓前面valatile變量的修改對(duì)其他cpu立即可見。

? ? ? ? 這段基本是書中原文,這樣說(shuō)可能有的同學(xué)不太懂(我就沒(méi)懂),下面在結(jié)合具體代碼說(shuō)下,instance = new Singleton() 這句代碼并不是一個(gè)原子操作,大致做了3件事情

1)給Singleton實(shí)例分配內(nèi)存空間

2)初始化Singleton對(duì)象

3)將instance對(duì)象指向分配的內(nèi)存空間

由于Java虛擬機(jī)指令重排序優(yōu)化,使得2、3的順序是無(wú)法保證的,如果出現(xiàn)了132的情況,并且在3執(zhí)行完畢2未執(zhí)行之前線程切換了,這個(gè)時(shí)候instance非空,所以直接返回instance,結(jié)果可想而知

? ? 避免篇幅過(guò)長(zhǎng)就到這吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第6章類文件結(jié)構(gòu) 6.1 概述 6.2 無(wú)關(guān)性基石 6.3 Class類文件的結(jié)構(gòu) java虛擬機(jī)不和包括java...
    kennethan閱讀 1,070評(píng)論 0 2
  • 轉(zhuǎn)載自 IBM Java 理論與實(shí)踐: 正確使用 Volatile 變量 你真的了解volatile關(guān)鍵字嗎? v...
    LongHuang閱讀 1,530評(píng)論 1 4
  • 昨天去考試,在考場(chǎng)上遇到之前的老師,上了歲數(shù)一老頭一大早站在考場(chǎng)外,生怕學(xué)生臨時(shí)有什么問(wèn)題,他可以回答他們的問(wèn)題。...
    許多的yolyol閱讀 207評(píng)論 1 1
  • 十七歲的某一天,那年盛夏,那年心動(dòng),某月相識(shí),某月回首,五日定情,二十二日綿長(zhǎng),許你安好。放蕩不羈獨(dú)愛(ài)自由,零碎不...
    淑媛媛閱讀 293評(píng)論 1 2

友情鏈接更多精彩內(nèi)容