【并發(fā)編程】volatile

volatile的作用

1纲辽、保證變量可見性

說到volatile,就不得不提一個(gè)詞:“可見性”,可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí)轧邪,一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值羞海。

要具體理解這句話的含義忌愚,還需要看下Java的內(nèi)存模型:

java內(nèi)存模型

由于內(nèi)存和cpu之間的計(jì)算速度差距過大,如果cpu直接從內(nèi)存中讀取數(shù)據(jù)十分影響性能却邓,所以在cpu和內(nèi)存之間加了一個(gè)溝通的橋梁——高速緩存硕糊。

Java內(nèi)存模型規(guī)定:

1.所有的變量都存儲(chǔ)在主內(nèi)存中;

2.線程擁有自己的工作內(nèi)存(即高速緩存)腊徙,線程的工作內(nèi)存中保存了該線程所使用到的變量(拷貝自主內(nèi)存)简十;

3.線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,不同線程之間也無法直接訪問對(duì)方工作內(nèi)存中的變量撬腾,線程間變量值的傳遞均需要通過主內(nèi)存來完成螟蝙。

單線程情況下,不會(huì)產(chǎn)生任何問題民傻,但當(dāng)多線程訪問同一變量時(shí)胰默,就有可能讀到臟數(shù)據(jù)。

假設(shè)有一個(gè)成員變量i饰潜,初值為0初坠,線程A和線程B同時(shí)修改一個(gè)變量i,首先彭雾,線程A碟刺、B將變量i從主存讀到工作內(nèi)存中,初值均為0薯酝,線程A將i+10半沽,線程B將i+1爽柒,此時(shí),由于線程A可能還沒有將工作內(nèi)存中的數(shù)據(jù)(i=10)刷新到主存者填,B沒有重新拷貝主存中的數(shù)據(jù)浩村,導(dǎo)致B線程看到的i仍然為0,也就是說占哟,A線程修改的變量對(duì)B線程“不可見”了心墅,即出現(xiàn)“臟讀”。

此時(shí)榨乎,如果對(duì)成員變量i加volatile關(guān)鍵字怎燥,同樣是上述場(chǎng)景,i在每次被線程訪問時(shí)蜜暑,都檢查變量的地址是否改變铐姚,如改變,就強(qiáng)迫從主內(nèi)存中重讀該成員變量的值肛捍,并且隐绵,當(dāng)成員變量發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫到主內(nèi)存中拙毫,這樣在任何時(shí)刻依许,AB線程總是看到某個(gè)成員變量的同一個(gè)值。

2恬偷、禁止指令重排序

1.當(dāng)程序執(zhí)行到volatile變量的讀操作或者寫操作時(shí)悍手,在其前面的操作的更改肯定全部已經(jīng)進(jìn)行,且結(jié)果已經(jīng)對(duì)后面的操作可見袍患;并且在其后面的操作肯定還沒有進(jìn)行坦康。

2.在進(jìn)行指令優(yōu)化時(shí),不能將在對(duì)volatile變量的讀操作或者寫操作的語句放在其后面執(zhí)行诡延,也不能把volatile變量后面的語句放到其前面執(zhí)行滞欠。

在Java內(nèi)存模型中,為了保證效率肆良,允許編譯器和處理器對(duì)指令進(jìn)行重排序筛璧,但是重排序過程不會(huì)影響到單線程程序的執(zhí)行,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性惹恃。

例如我們常見的雙重檢查單例:

雙重檢查單例

為什么要給instance加上volatile關(guān)鍵字呢夭谤?這是由于instance=new Singleton();這句話不是原子操作,在JVM中巫糙,這句話被分為三個(gè)階段:

1.為instance分配內(nèi)存

2.初始化instance

3.將instance變量指向分配的內(nèi)存空間

由于JVM重排序的存在朗儒,上述23兩步操作是沒有依賴關(guān)系的,可能被重排序,也就是說醉锄,在instance還沒被初始化的時(shí)候乏悄,instance就已經(jīng)不為null了,這時(shí)恳不,另一個(gè)線程執(zhí)行到第一個(gè)if檩小,判斷不為null,直接return instance烟勋,導(dǎo)致異常规求。

volatile的實(shí)現(xiàn)原理

1、可見性

對(duì)聲明了volatile變量進(jìn)行寫操作時(shí)卵惦,JVM會(huì)向處理器發(fā)送一條Lock前綴的指令颓哮,將這個(gè)變量所在緩存行的數(shù)據(jù)回寫到系統(tǒng)內(nèi)存, 這一步確保了如果有其他線程對(duì)聲明了volatile變量進(jìn)行修改鸵荠,則立即更新主內(nèi)存中數(shù)據(jù)。

但這時(shí)候其他處理器的緩存還是舊的伤极,所以在多處理器環(huán)境下蛹找,為了保證各個(gè)處理器緩存一致,每個(gè)處理會(huì)通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己的緩存是否過期哨坪,當(dāng)處理器發(fā)現(xiàn)自己緩存行對(duì)應(yīng)的內(nèi)存地址被修改了庸疾,就會(huì)將當(dāng)前處理器的緩存行設(shè)置成無效狀態(tài),當(dāng)處理器要對(duì)這個(gè)數(shù)據(jù)進(jìn)行修改操作時(shí)当编,會(huì)強(qiáng)制重新從系統(tǒng)內(nèi)存把數(shù)據(jù)讀到處理器緩存里届慈。 這一步確保了其他線程獲得的聲明了volatile變量都是從主內(nèi)存中獲取最新的。

2忿偷、有序性

Lock前綴指令實(shí)際上相當(dāng)于一個(gè)內(nèi)存屏障金顿,它確保指令重排序時(shí)不會(huì)把其后面的指令排到內(nèi)存屏障之前的位置,也不會(huì)把前面的指令排到內(nèi)存屏障的后面鲤桥;即在執(zhí)行到內(nèi)存屏障這句指令時(shí)揍拆,在它前面的操作已經(jīng)全部完成。

volatile的使用條件

1.對(duì)變量的寫操作不依賴于當(dāng)前值茶凳;

2.該變量沒有包含在具有其他變量的不變式中嫂拴。

volatile的注意點(diǎn)

上面一條講到了volatile關(guān)鍵字的使用條件,從注意點(diǎn)的角度來解釋一下贮喧。

首先筒狠,volatile雖然可以用在并發(fā)編程中,但是不能代替synchronized關(guān)鍵字箱沦,因?yàn)関olatile關(guān)鍵字只能保證并發(fā)編程中三大概念中的兩個(gè)辩恼,即可見性和有序性,但是它不能保證程序的原子性,舉例說明:

不能保證原子性的volatile

該例中运挫,inc的結(jié)果為10000嗎状共?不是,那問題出在哪里谁帕?

問題在于峡继,increase方法中,inc++這個(gè)操作不具備原子性匈挖,線程要先讀到位于主內(nèi)存中的inc的值碾牌,然后對(duì)其進(jìn)行+1,然后再將其放回主內(nèi)存中儡循,在這三步中舶吗,假設(shè)線程A讀取了inc,正在進(jìn)行+1操作择膝,此時(shí)誓琼,線程B讀取inc,由于線程A沒有操作完肴捉,所有并沒有將+1后的操作寫入主存腹侣,導(dǎo)致B讀取的仍為舊值,最終導(dǎo)致程序的結(jié)果小于預(yù)期結(jié)果齿穗,解決方法:可以將increase方法加上synchronized關(guān)鍵字傲隶,保證其原子性。

總結(jié)

本篇大致講解了volatile的用途以及實(shí)現(xiàn)原理窃页,上文也說到跺株,他不能代替synchronized關(guān)鍵字,在下篇文章中脖卖,就要講到Java中的各種鎖乒省,看下Java的鎖機(jī)制是如何保證效率的情況下,在多線程中安全運(yùn)行的畦木。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末作儿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子馋劈,更是在濱河造成了極大的恐慌攻锰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妓雾,死亡現(xiàn)場(chǎng)離奇詭異娶吞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)械姻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門妒蛇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事绣夺±艏椋” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵陶耍,是天一觀的道長(zhǎng)奋蔚。 經(jīng)常有香客問我,道長(zhǎng)烈钞,這世上最難降的妖魔是什么泊碑? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮毯欣,結(jié)果婚禮上馒过,老公的妹妹穿的比我還像新娘。我一直安慰自己酗钞,他們只是感情好腹忽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著砚作,像睡著了一般留凭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上偎巢,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音兼耀,去河邊找鬼压昼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瘤运,可吹牛的內(nèi)容都是我干的窍霞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼拯坟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼但金!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起郁季,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤冷溃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后梦裂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體似枕,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年年柠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凿歼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖答憔,靈堂內(nèi)的尸體忽然破棺而出味赃,到底是詐尸還是另有隱情,我是刑警寧澤虐拓,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布心俗,位于F島的核電站,受9級(jí)特大地震影響侯嘀,放射性物質(zhì)發(fā)生泄漏另凌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一戒幔、第九天 我趴在偏房一處隱蔽的房頂上張望吠谢。 院中可真熱鬧,春花似錦诗茎、人聲如沸工坊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽王污。三九已至,卻和暖如春楚午,著一層夾襖步出監(jiān)牢的瞬間昭齐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工矾柜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阱驾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓怪蔑,卻偏偏與公主長(zhǎng)得像里覆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缆瓣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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