淺談volatile

在討論volatile之前我們先來了解下cpu與內(nèi)存之間的關(guān)系:


CPU.png

手殘黨勋桶、圖丑散罕、大家心中有個(gè)大概就行了挑随。

圖中的緩存為cpu緩存状您,實(shí)際上電腦一般設(shè)有三級(jí)緩存。cpu緩存為于cpu和內(nèi)存之間的臨時(shí)存儲(chǔ)器兜挨,它的容量很小但交換速度卻比內(nèi)存快得多膏孟。

緩存的出現(xiàn)主要是為了解決CPU運(yùn)算速度與內(nèi)存讀寫速度不匹配的矛盾,因?yàn)镃PU運(yùn)算速度要比內(nèi)存讀寫速度快很多拌汇,這樣會(huì)使CPU花費(fèi)很長(zhǎng)時(shí)間等待數(shù)據(jù)到來或把數(shù)據(jù)寫入內(nèi)存柒桑。具體大家可自行百度。

volatile提供的三個(gè)特性:

  • 原子性:

    一個(gè)很好的例子是:32位機(jī)上的long類型讀寫操作是分為高低位讀寫兩次的担猛,并且不原子性幕垦。所謂原子性是指一個(gè)集合操作中所有操作“同生共死”丢氢、要么一起成功執(zhí)行要么一起不執(zhí)行傅联。

    long i = 0; i = 10;

    當(dāng)有一條線程執(zhí)行到i = 10時(shí),首先會(huì)為低16位進(jìn)行賦值疚察,倘若此時(shí)有另一條線程來讀取時(shí)只會(huì)讀到只賦值的低16位的數(shù)據(jù)蒸走,從而造成bug的出現(xiàn)。
    不過volatile并不能代替鎖貌嫡,它無(wú)法保證復(fù)合操作的原子性比驻,例如:i++,實(shí)際上此操作是由三個(gè)步驟組成的:首先取得i的值该溯,再對(duì)i進(jìn)行+1,再將結(jié)果寫回i别惦。

  • 可見性:

    就如上圖所示狈茉,每個(gè)CPU都有屬于自己的緩存

    int i = 0;
    線程1執(zhí)行的代碼
    i = 10;
    
    線程2執(zhí)行的代碼
    j = i;
    

    假設(shè)線程1先于線程2執(zhí)行,并且CPU1執(zhí)行線程1掸掸、CPU2執(zhí)行線程2氯庆。

    當(dāng)線程1執(zhí)行 i =10這句時(shí),會(huì)先把i的初始值加載到CPU1的高速緩存中扰付,然后賦值為10堤撵,那么在CPU1的高速緩存當(dāng)中i的值變?yōu)?0了,卻沒有立即寫入到內(nèi)存當(dāng)中羽莺。

此時(shí)線程2執(zhí)行 j = i实昨,它會(huì)先去主存讀取i的值并加載到CPU2的緩存當(dāng)中,注意此時(shí)內(nèi)存當(dāng)中i
  的值還是0盐固,那么就會(huì)使得j的值為0荒给,而不是10。

這就是可見性問題刁卜,線程1對(duì)變量i修改了之后锐墙,線程2沒有立即看到線程1修改的值。
  
  當(dāng)一個(gè)共享變量被volatile修飾時(shí)长酗,它會(huì)保證修改的值會(huì)立即被更新到內(nèi)存中溪北,當(dāng)有其他線程需
  要讀取時(shí),它會(huì)去內(nèi)存中讀取新值夺脾。

  • 有序性:

    大部分人會(huì)認(rèn)為程序執(zhí)行順序會(huì)和代碼編寫順序一樣之拨,其實(shí)不然。在JMM內(nèi)存模型中允許編譯器和處理器對(duì)指令進(jìn)行重新排序來進(jìn)行優(yōu)化咧叭、以便于CPU能夠并行執(zhí)行指令蚀乔,從而提高效率。

    在單線程中重排序的結(jié)果不會(huì)影響程序的運(yùn)行結(jié)果菲茬,但卻會(huì)影響多線程并行執(zhí)行的正確性了吉挣。
    舉個(gè)簡(jiǎn)單的栗子:

    Thread 1    Thread 2
    1:r2 = A    3:r1 = b
    2:b = 1     4:A = 2
    

    從順序上看 (r2 == 2) 、(r1 == 1) 應(yīng)該不可能出現(xiàn)婉弹,但如果被重排序成下列順序是就不一定了:

    Thread 1    Thread 2
    b = 1       r1 = b
    r2 = A      A = 2
    

    再舉一個(gè)稍微復(fù)雜的例子:

    class order {
        int a = 0;
        boolean flag = false;
        public void writer() {
            a = 1;
            flag = true;
        }
        public void reader() {
            if (flag) {
                int i = a + 1;
                dosomething;
            }
        }
    }
    

    假設(shè)線程A先執(zhí)行writer()方法睬魂,然后線程B執(zhí)行reader()方法。當(dāng)發(fā)生指令重排序后镀赌,writer()方法中的flag的寫入可能會(huì)先于a的寫入氯哮,造成線程B在執(zhí)行reader方法時(shí)判斷正確并為i賦值。

    指令重排序帶來的可見性問題

    雖然指令重排序會(huì)帶來許多問題商佛,但卻能有效提高效率喉钢,并且在串行代碼中大家可放心:

    指令重排序可以保證串行語(yǔ)義一致姆打,但沒有義務(wù)保證多線程之間的語(yǔ)義也一致。

Java虛擬機(jī)還規(guī)定了些規(guī)則指定了哪些指令不能重排序

Happen-Before 規(guī)則:

  • 程序順序原則:一個(gè)線程內(nèi)保證語(yǔ)義的串行性
  • volatile規(guī)則:volatile變量的寫肠虽,先發(fā)送于讀棒假,保證volatile變量的可見性
  • 鎖規(guī)則:解鎖必然先發(fā)生于隨后的加鎖前
  • 傳遞性:A先于B勺爱,B先于C甲献,那么A必然先于C
  • 線程的start()方法先于它的每一個(gè)動(dòng)作
  • 線程的中斷先于被中斷程序的代碼
  • 對(duì)象的構(gòu)造函數(shù)的執(zhí)行镀首,結(jié)束先于finalized()方法

總的來說,volatile還涉及到JMM內(nèi)存模型等相關(guān)知識(shí)伯复。推薦大家去看《Java并發(fā)編程的藝術(shù)》和《Java高并發(fā)程序設(shè)計(jì)》慨代,里面講解更加透徹。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啸如,一起剝皮案震驚了整個(gè)濱河市侍匙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叮雳,老刑警劉巖想暗,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異帘不,居然都是意外死亡说莫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門寞焙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來储狭,“玉大人,你說我怎么就攤上這事捣郊×杀罚” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵呛牲,是天一觀的道長(zhǎng)刮萌。 經(jīng)常有香客問我,道長(zhǎng)娘扩,這世上最難降的妖魔是什么着茸? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮琐旁,結(jié)果婚禮上涮阔,老公的妹妹穿的比我還像新娘。我一直安慰自己旋膳,他們只是感情好澎语,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布途事。 她就那樣靜靜地躺著验懊,像睡著了一般擅羞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上义图,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天减俏,我揣著相機(jī)與錄音,去河邊找鬼碱工。 笑死娃承,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的怕篷。 我是一名探鬼主播历筝,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼廊谓!你這毒婦竟也來了梳猪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蒸痹,失蹤者是張志新(化名)和其女友劉穎春弥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叠荠,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匿沛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榛鼎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逃呼。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖者娱,靈堂內(nèi)的尸體忽然破棺而出蜘渣,到底是詐尸還是另有隱情,我是刑警寧澤肺然,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布蔫缸,位于F島的核電站,受9級(jí)特大地震影響际起,放射性物質(zhì)發(fā)生泄漏拾碌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一街望、第九天 我趴在偏房一處隱蔽的房頂上張望校翔。 院中可真熱鬧,春花似錦灾前、人聲如沸防症。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蔫敲。三九已至饲嗽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奈嘿,已是汗流浹背貌虾。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裙犹,地道東北人尽狠。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像叶圃,于是被迫代替她去往敵國(guó)和親袄膏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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