Java并發(fā)之二:JVM視角下的volatile

了解volatile語(yǔ)義對(duì)了解多線程的其他特性很有意義,所以把它放在前面討論。

volatile是JVM提供的最輕量級(jí)的同步機(jī)制。volatile提供單個(gè)field的內(nèi)存同步控制,synchronized則提供整個(gè)臨界區(qū)(代碼塊/方法)的同步控制。

2個(gè)基本特性

當(dāng)一個(gè)變量定義為volatile之后,它將具備兩種特性,第一是確保了此變量對(duì)所有線程的(實(shí)時(shí))可見(jiàn)性1。普通變量做不到這一點(diǎn),普通變量的值在線程間傳遞是異步的,需要通過(guò)主內(nèi)存來(lái)完成,例如,線程A修改一個(gè)普通變量值,然后向主內(nèi)存進(jìn)行回寫,另外一條線程B在線程A回寫完成之后再?gòu)闹鲀?nèi)存進(jìn)行讀取操作,新變量值才會(huì)對(duì)線程B可見(jiàn)。雖然volatile變量在各個(gè)線程的工作內(nèi)存中不存在一致性問(wèn)題,但是Java里的運(yùn)算并非全是原子操作,例如復(fù)合操作int++,導(dǎo)致volatile變量的運(yùn)算在并發(fā)下一樣是不安全的。

《深入理解Java虛擬機(jī)》p366?中舉了一個(gè)例子:代碼發(fā)起20個(gè)新線程,每個(gè)線程對(duì)volatile變量(race,初始值=0)進(jìn)行10000次自增,如果代碼能正確并發(fā)的話,正確結(jié)果應(yīng)該是200000,但實(shí)際并非如此,誤差極大(15W+ ~ 18W+)。問(wèn)題出在race++自增運(yùn)算中。當(dāng)指令把race值取到操作棧頂時(shí),volatile關(guān)鍵字保證了race的值在此時(shí)是正確的,但是在執(zhí)行iconst_1,iadd這些指令時(shí),其他線程可能已經(jīng)把race的值加大了,而在操作棧頂?shù)闹稻妥兂闪诉^(guò)期的數(shù)據(jù),所以putstatic指令執(zhí)行后就可能把較小的race值同步回主內(nèi)存之中。即使編譯出來(lái)的只有一條字節(jié)碼指令,也不意味著它是一個(gè)原子操作。一條字節(jié)碼指令也可能轉(zhuǎn)化成若干條本地機(jī)器碼指令。

由于volatile變量只能保證可見(jiàn)性,在不符合以下兩條規(guī)則的運(yùn)算場(chǎng)景中,仍然要通過(guò)加鎖來(lái)保證原子性,如synchronized或juc原子類。

? ? 1、運(yùn)算結(jié)果并不依賴變量的當(dāng)前值,或者能夠確保只有單一線程修改變量的值。
? ? 2、變量不需要與其他的狀態(tài)變量共同參與不變約束。

volatile的第二個(gè)語(yǔ)義是禁止指令重排序優(yōu)化。普通變量?jī)H僅會(huì)保證在該方法的執(zhí)行過(guò)程中所有依賴賦值結(jié)果的地方都能獲取到正確的結(jié)果,而不能保證變量賦值操作的順序與程序代碼中的執(zhí)行順序一致。就是說(shuō)JVM會(huì)保證有賦值依賴關(guān)系的操作順序,但如果只有業(yè)務(wù)依賴關(guān)系的話,JVM是無(wú)法識(shí)別,也就無(wú)法保證了。

《深》p369中舉的例子,線程A讀取配置文件,然后將initialized置為true,線程B會(huì)一直檢查initialized,如為true則開(kāi)始其他操作。這個(gè)邏輯關(guān)系JVM是無(wú)法感知的,因?yàn)樽x取配置文件與initialized賦值之間并無(wú)賦值依賴關(guān)系。如果initialized變量沒(méi)有使用volatile修飾,就可能由于指令重排序的優(yōu)化,導(dǎo)致線程A最后一句initialized=true對(duì)應(yīng)的指令被提前執(zhí)行,這樣線程線程B中使用配置信息的代碼就可能出現(xiàn)錯(cuò)誤,而volatile關(guān)鍵字則可以避免此類情況。

對(duì)于一段DCL實(shí)現(xiàn)單例的代碼,通過(guò)對(duì)比加入volatile和未加入volatile時(shí)的匯編代碼的差別,發(fā)現(xiàn)關(guān)鍵變化在于有volatile修飾的變量,賦值后多了一個(gè)“l(fā)ock addl $0x0, (%esp)”操作,這個(gè)操作相當(dāng)于一個(gè)內(nèi)存屏障(指重排序時(shí)不能把后面的指令重排序到內(nèi)存屏障之前的位置)。“l(fā)ock addl”指令的作用是使得本CPU的Cache寫入內(nèi)存,該寫入動(dòng)作也會(huì)引起其他CPU無(wú)效化其Cache,相當(dāng)于對(duì)Cache變量做了一次store+write操作,讓前面volatile變量的操作對(duì)其他CPU立即可見(jiàn)。

JMM對(duì)volatile變量定義的特殊規(guī)則

假定T表示一個(gè)線程,V和W分別表示兩個(gè)volatile變量,那么在進(jìn)行read、load、use、assign、store、write操作時(shí)需要滿足以下規(guī)則:

1、load + use 必須成對(duì)出現(xiàn)。這條規(guī)則要求在工作內(nèi)存中,每次使用V前都必須先從主內(nèi)存中刷新最新的值,用于保證能看見(jiàn)其他線程對(duì)變量V所做的修改后的值。

2、assign + store 必須成對(duì)出現(xiàn)。這條規(guī)則要求在工作內(nèi)存中,每次修改V后都必須立刻同步回主內(nèi)存中,用于保證其他線程可以看到自己對(duì)變量V的修改。

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


注1:JVM對(duì)“實(shí)時(shí)可見(jiàn)性”的實(shí)現(xiàn),并非是絕對(duì)的。在各個(gè)線程的工作內(nèi)存中,volatile變量可以存在不一致,但每次使用前都要先刷新,執(zhí)行引擎看不到不一致的情況,因此可以認(rèn)為不存在一致性問(wèn)題。

注2:volatile屏蔽指令重排序的語(yǔ)義在JDK1.5中才被完全修復(fù),此前的JDK無(wú)法保證volatile變量完成避免重排序?qū)е碌膯?wèn)題,這點(diǎn)也是在JDK1.5之前無(wú)法安全使用DCL(雙鎖檢測(cè))來(lái)實(shí)現(xiàn)單例模式的原因。


參考資料

? ? ?《深入理解Java虛擬機(jī)》第二版 12.3.3小節(jié)

最后編輯于
?著作權(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)容

  • (本文原發(fā)在LOFTER,今日翻閱自己的簡(jiǎn)書,恰好有幾個(gè)可憐的回顧片段,索性將2015年的年度總結(jié)也復(fù)制過(guò)來(lái)。) ...
    釣譽(yù)閱讀 388評(píng)論 0 0
  • 今天很難得醒的特別早,五點(diǎn)多就從一個(gè)好夢(mèng)里爬出來(lái),睜開(kāi)眼天剛蒙蒙亮。這么安靜的清晨已經(jīng)好久不見(jiàn)了,資深懶蟲的我平時(shí)...
    yanzikuaile燕子閱讀 431評(píng)論 3 2
  • 什么是理想生活,我現(xiàn)在講的比較少了,原來(lái)這個(gè)話題我經(jīng)常給先生說(shuō),我想不欠賬的日子很踏實(shí),應(yīng)該是幸福的,不交房租的日...
    悠然_3c09閱讀 343評(píng)論 2 2

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