volatile詳解

前言

被volatile修飾的變量能夠保證每個線程能夠獲取該變量的最新值百炬,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象褐隆。

相信很多人都用過volatile這個關(guān)鍵字,也知道它的妙用剖踊,但是其底層原理是否知曉呢庶弃?通過這篇文章就一目了然了。 在了解volatile之前CPU多及緩存架構(gòu)和JMM內(nèi)存模型德澈,如果不了解的在我的其他文章里面有講到這兩點歇攻。

這里很直觀的感受到volatile的效果:被volatile修飾的變量能夠保證每個線程能夠獲取該變量的最新值。 當(dāng)flag加了volatile關(guān)鍵字修飾時镇辉,線程2修改完flag的值之后線程1能感受到flag變量被修改了屡穗,為什么會這樣子呢?下面結(jié)合JMM原子性操作對實現(xiàn)機(jī)制進(jìn)行深入講解忽肛。

  1. 聲明一個變量flag = true鸡捐,存儲在主內(nèi)存中

  2. 通過read方法讀取變量flag=true(線程1、2)

  3. 通過load方法加載變量flag=true到各自線程的工作內(nèi)存(線程1麻裁、2)

  4. 通過use方法使用本線程工作內(nèi)存中變量flag=true(線程1進(jìn)入死循環(huán)狀態(tài)箍镜,知道flag變成了false)

  5. 線程2通過assign方法對本地變量flag進(jìn)行計算并賦值flag=false

  6. 線程2準(zhǔn)備通過store方法將本地變量flag=false寫入主內(nèi)存源祈,此時會調(diào)用lock方法對這塊內(nèi)存區(qū)域的緩存進(jìn)行鎖定(緩存行鎖定)。

  7. 通過lock對緩存行鎖定之后色迂,MESI總線嗅探機(jī)制監(jiān)聽到變量flag的值發(fā)生了變化香缺,此時會對所有使用該變量線程工作內(nèi)存中的flag變量進(jìn)行失效,導(dǎo)致其他線程需要從2步驟開始重新從主內(nèi)存獲取flag變量(其他線程從主內(nèi)存獲取flag變量的時機(jī)需要等待緩存行解鎖歇僧,如果此處不對緩存行進(jìn)行鎖定图张,那么其他線程去讀取主內(nèi)存時可能獲取到的變量還是原來的值)。

  8. 線程2通過write方法對主內(nèi)存變量flag進(jìn)行修改(對flag變量重新賦值)

  9. 線程2調(diào)用unlock方法诈悍,對鎖定的緩存行進(jìn)行解鎖祸轮,解鎖完成之后其他線程從主內(nèi)存重新獲取變量flag=false。

volatile主要通過匯編lock前綴指令侥钳,它會鎖定當(dāng)前內(nèi)存區(qū)域的緩存(緩存行)适袜,并且立即將當(dāng)前緩存行數(shù)據(jù)寫入主內(nèi)存(耗時非常短),回寫主內(nèi)存的時候會通過MESI協(xié)議使其他線程緩存了該變量的地址失效舷夺,從而導(dǎo)致其他線程需要重新去主內(nèi)存中重新讀取數(shù)據(jù)到其工作線程中苦酱。

volatile原子性分析

volatile能保證原始數(shù)據(jù)類型賦值的原子性,無法保證復(fù)合操作的原子性给猾。

上面程序運(yùn)行最終結(jié)果都會小于等于10000疫萤,為什么會出現(xiàn)這種情況呢,罪魁禍?zhǔn)拙褪窃诓l(fā)情況下違背了原子性操作導(dǎo)致敢伸,接下來一步一步進(jìn)行分析原由扯饶。

  1. 線程的運(yùn)行有隨機(jī)性,假設(shè)此時線程1池颈、2尾序、3都從主內(nèi)存讀取變量i=0到了各自線程的工作內(nèi)存

  2. 線程1、2饶辙、3都開始通過assign對本地變量進(jìn)行賦值操作蹲诀,此時各自線程內(nèi)都變量i的值都變成了1

  3. 線程2優(yōu)先調(diào)用原子方法store進(jìn)行變量的主內(nèi)存回寫操作斑粱,此時通過lock對緩存行(i的緩存)進(jìn)行鎖定弃揽,由于線程2優(yōu)先搶到了鎖,導(dǎo)致線程1则北、3不能將其工作內(nèi)存的變量i寫回到主內(nèi)存矿微,并且此時通過CPU總線MESI協(xié)議將其工作內(nèi)存變量i的地址失效,導(dǎo)致線程1尚揣、3的本次計算結(jié)果丟失

  4. 同理涌矢,線程4后執(zhí)行,讀取到了線程2修改變量i的值為2之后執(zhí)行計算快骗,當(dāng)有其他線程搶先lock寫入內(nèi)存的時候娜庇,線程4的計算結(jié)果也會丟失塔次,在這些巧合之下導(dǎo)致i最終計算結(jié)果會小于等于10000。

只需要在add方法處加上synchronized關(guān)鍵字進(jìn)行修改名秀,保證add方法的原子性就能保證每次執(zhí)行結(jié)果都是10000励负。synchronized回在后面的文章中講到

volatile有序性分析

  1. [a=0,b=0]:兩個線程同時執(zhí)行到語句1和與語句4,此時從主內(nèi)存獲取到變量x匕得、y值為0

  2. [a=1,b=0]:線程2先執(zhí)行語句3继榆、4,此時讀取從主內(nèi)存獲取到變量x值為0汁掠,并為變量b賦值0略吨,之后賦y為1,并將=1寫入到主內(nèi)存考阱,之后線程1執(zhí)行到語句1翠忠,從主內(nèi)存獲取y=1賦值給a

  3. [a=0,b=1]:線程1先執(zhí)行語句1、2羔砾,此時讀取從主內(nèi)存獲取到變量y值為0负间,并為變量a賦值0,之后賦x為1姜凄,并將=1寫入到主內(nèi)存政溃,之后線程2執(zhí)行到語句3,從主內(nèi)存獲取x=1賦值給b

  4. 語句1和語句2進(jìn)行了指令重排态秧,導(dǎo)致語句1優(yōu)先與語句2先執(zhí)行董虱,語句3、4同理申鱼,此時主內(nèi)存中x愤诱、y都變成了1,之后a捐友、b賦值的時候都取到了1

當(dāng)x淫半、y通過volatile進(jìn)行修飾時

執(zhí)行的結(jié)果就只有三種情況了[a=0,b=0, a=1,b=0, a=0,b=1]
volatile修飾變量之后回禁止指令重排,語句1一定在語句2之前執(zhí)行匣砖,語句3一定會在語句4之前執(zhí)行科吭。

volatile重排序規(guī)則表(針對編譯器重排序):

上面的例子符合第二個volatile寫第一個普通寫規(guī)則,編譯器不會進(jìn)行重排序猴鲫。

為了實現(xiàn)volatile內(nèi)存語義時对人,編譯器在生成字節(jié)碼時,會在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序拂共。對于編譯器來說牺弄,發(fā)現(xiàn)一個最優(yōu)布置來最小化插入屏障的總數(shù)幾乎是不可能的,為此宜狐,JMM采取了保守策略:

  1. 在每個volatile寫操作的前面插入一個StoreStore屏障势告;

  2. 在每個volatile寫操作的后面插入一個StoreLoad屏障蛇捌;

  3. 在每個volatile讀操作的后面插入一個LoadLoad屏障;

  4. 在每個volatile讀操作的后面插入一個LoadStore屏障咱台。

需要注意的是:volatile寫是在前面和后面分別插入內(nèi)存屏障豁陆,而volatile讀操作是在后面插入兩個內(nèi)存屏障

StoreStore屏障:禁止上面的普通寫和下面的volatile寫重排序;

StoreLoad屏障:防止上面的volatile寫與下面可能有的volatile讀/寫重排序

LoadLoad屏障:禁止下面所有的普通讀操作和上面的volatile讀重排序

LoadStore屏障:禁止下面所有的普通寫操作和上面的volatile讀重排序


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吵护,一起剝皮案震驚了整個濱河市盒音,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌馅而,老刑警劉巖祥诽,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓮恭,居然都是意外死亡雄坪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門屯蹦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來维哈,“玉大人,你說我怎么就攤上這事登澜±樱” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵脑蠕,是天一觀的道長购撼。 經(jīng)常有香客問我,道長谴仙,這世上最難降的妖魔是什么迂求? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮晃跺,結(jié)果婚禮上揩局,老公的妹妹穿的比我還像新娘。我一直安慰自己掀虎,他們只是感情好凌盯,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涩盾,像睡著了一般十气。 火紅的嫁衣襯著肌膚如雪励背。 梳的紋絲不亂的頭發(fā)上春霍,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音叶眉,去河邊找鬼址儒。 笑死芹枷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的莲趣。 我是一名探鬼主播鸳慈,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼喧伞!你這毒婦竟也來了走芋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤潘鲫,失蹤者是張志新(化名)和其女友劉穎翁逞,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溉仑,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡挖函,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了浊竟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怨喘。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖振定,靈堂內(nèi)的尸體忽然破棺而出必怜,到底是詐尸還是另有隱情,我是刑警寧澤后频,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布棚赔,位于F島的核電站,受9級特大地震影響徘郭,放射性物質(zhì)發(fā)生泄漏靠益。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一残揉、第九天 我趴在偏房一處隱蔽的房頂上張望胧后。 院中可真熱鬧,春花似錦抱环、人聲如沸壳快。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽眶痰。三九已至,卻和暖如春梯啤,著一層夾襖步出監(jiān)牢的瞬間竖伯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留七婴,地道東北人祟偷。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像打厘,于是被迫代替她去往敵國和親修肠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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

  • 本文從計算機(jī)模型開始户盯,以及CPU與內(nèi)存嵌施、IO總線之間的交互關(guān)系到CPU緩存一致性協(xié)議的邏輯進(jìn)行了闡述,并對JMM的...
  • volatile volatile是Java虛擬機(jī)提供的輕量級的同步機(jī)制莽鸭,保證可見性艰管,不保證原子性,禁止指令重排(...
    西界__閱讀 148評論 0 1
  • volatile關(guān)鍵字修飾的共享變量主要有兩個特點:1.保證了不同線程訪問的內(nèi)存可見性 2.禁止重排序 在說內(nèi)存可...
    fad2aa506f5e閱讀 264評論 0 0
  • 一蒋川、volatile的作用詳解 1牲芋、防重排序 在并發(fā)環(huán)境下的單例實現(xiàn)方式,我們通侈嗲颍可以采用雙重檢查加鎖(D...
    淡若飄絮閱讀 154評論 0 1
  • 今天感恩節(jié)哎缸浦,感謝一直在我身邊的親朋好友。感恩相遇氮兵!感恩不離不棄裂逐。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,556評論 0 11