Volatile關(guān)鍵字詳解

volatile是Java中一個(gè)非常重要的關(guān)鍵字窝剖,要想完全搞懂volatile的作用,需要一些額外的輔助知識(shí)朴读。

要了解volatile似踱,就要先對(duì)CPU,CPU cache和主存有所有了解。

主存就是我們平時(shí)說的內(nèi)存墓臭,它的讀寫速度很慢蘸鲸,而CPU運(yùn)算速度很快,為了彌補(bǔ)二者的差異窿锉,在CPU和主存之間引入了CPU Cache,
現(xiàn)在很多CPU都會(huì)有三層Cache,分別稱為L(zhǎng)1,L2和L3酌摇,其中L1最靠近CPU,緩存速度也最快嗡载,L1這塊緩存被分為了兩個(gè)部分窑多,一部分是指令緩存,稱為L(zhǎng)1i,一部分是數(shù)據(jù)緩存洼滚,稱為L(zhǎng)1d.

只有有緩存埂息,就會(huì)出現(xiàn)緩存不一致的問題,比如i++這個(gè)操作遥巴。

在有緩存時(shí)候的計(jì)算流程是:

  1. 從主存中讀取i的值到緩存中
  2. 在緩存中把i的值加1
  3. 把緩存中的值刷回到主存中耿芹。

這樣的模型在單線程下沒有任何問題,但是在多線程中挪哄,就會(huì)出現(xiàn)問題吧秕,比如在步驟二中執(zhí)行,此時(shí)有一個(gè)線程直接給i重新賦值了迹炼,這時(shí)候就會(huì)出現(xiàn)問題砸彬。

解決這個(gè)問題的一個(gè)有效辦法就是 緩存一致性協(xié)議,這個(gè)協(xié)議大體思路是:

  1. 當(dāng)CPU操作Cache中的副本時(shí)斯入,會(huì)去查看該副本是不是在其他Cache中也存在一份
  2. 如果不存在的話砂碉,CPU就可以對(duì)Cached中的副本進(jìn)行任意的操作了
  3. 如果存在的話, CPU先把Cached中的副本讀取到自己的寄存器中刻两,然后通知其他的CPU說"你們保存的Cache無效了"
  4. 其他CPU收到這個(gè)通知之后增蹭,如果要操作這個(gè)變量的副本,就會(huì)從主存里在讀取一遍磅摹。

那么我們接下來看一下Java的內(nèi)存模型滋迈,Java的內(nèi)存模型一個(gè)很重要的作用就是規(guī)定了:一個(gè)線程對(duì)共享變量(主內(nèi)存中的變量)的修改,何時(shí)對(duì)其他線程可見户誓。

具體來說就是:

  1. 每個(gè)線程都有自己的工作內(nèi)存饼灿,稱為緩存。
  2. 線程只能操作自己的工作內(nèi)存帝美,不能直接操作主內(nèi)存
  3. 共享變量在工作內(nèi)存中有一個(gè)副本
  4. JVM決定何時(shí)把工作內(nèi)存中的副本刷回主內(nèi)存碍彭。

接下來了解一下并發(fā)編程必須要掌握的幾個(gè)概念:

  1. 原子性
    一組操作要么不做,要么保證全部做完,不會(huì)被中斷
    volatile關(guān)鍵字無法保證原子性

  2. 可見性
    一個(gè)線程對(duì)一個(gè)變量進(jìn)行了修改庇忌,另一個(gè)線程可以馬上感知到這種修改舞箍。
    volatile關(guān)鍵字可以保證可見性

  3. 有序性
    主要就是防止進(jìn)行指令重排,因?yàn)闉榱藘?yōu)化執(zhí)行順序皆疹,JVM并不一定會(huì)按照書寫的順序去執(zhí)行创译,會(huì)進(jìn)行優(yōu)化,優(yōu)化的原則是:不保證執(zhí)行的順序墙基,只保證重排之后的執(zhí)行結(jié)果和重排之前一樣软族。

在多線程情況下,指令重排有時(shí)候會(huì)造成問題残制。所以有時(shí)候要禁止掉指令重排立砸。

最常見的就是通過一個(gè)變量來進(jìn)行初始化判斷,比如

private boolean isInit = false;
private Manager mManger;
public Manager getManager(){
  if(!isInit){
    mManger = initManager();
    isInit = true;
  }
  return mManager;
}

在上面那個(gè)例子中初茶,單線程情況下不會(huì)有任何問題颗祝,在多線程情況下,如果不進(jìn)行指令重排也不會(huì)有問題恼布。
但是如果進(jìn)行了指令重排螺戳,比如指令重排之后,把isInit = true放在了mManager = initManager()之上折汞,很可能在多線程的情況下出現(xiàn)mManager為空的情況倔幼,從而出現(xiàn)空指針異常。而且這種異常還很難發(fā)現(xiàn)爽待,通常大家都是一臉懵逼损同,說mManager肯定不可能為空呀。

那現(xiàn)在我們把多線程情況下的指令重排造成的mManager為空的情況說一下鸟款。

線程1執(zhí)行g(shù)etManager方法膏燃,由于指令重排,isInit = true先執(zhí)行何什,然后去真正初始化mManager,最后返回mManager實(shí)例组哩,不會(huì)有任何問題。
但是當(dāng)線程1執(zhí)行完isInit = true之后处渣,線程2開始執(zhí)行g(shù)etManager方法伶贰,發(fā)現(xiàn)isInit為true,就直接返回了mManager霍比,而此時(shí)mManager還沒有初始化幕袱,所以線程2中會(huì)出現(xiàn)空指針異常暴备。

所以建議悠瞬, 依賴一個(gè)變量來判斷是否初始化,而且這種判斷是運(yùn)行在多線程下的,那么該變量一定要是volatile的

接下來我們看一下JVM是如何通過內(nèi)存模型來保證原子性浅妆,可見性和有序性的望迎。

JVM保證對(duì)于變量的讀取和直接賦值都是原子性的,什么是直接賦值呢凌外,比如x = 1這就是直接賦值辩尊,JVM保證其原子性。但是y = x就不是原子性的康辑,因?yàn)檫@涉及到三個(gè)操作摄欲,第一步是把x的值讀入工作內(nèi)存,第二步是把x的值賦給y,第三步是把y的值刷回主內(nèi)存疮薇。
同理類似于i++,x=x+1都不是原子性操作胸墙。

如果要確保這些操作的原子性,都要使用synchronized關(guān)鍵字按咒。

JVM通過synchronized關(guān)鍵字迟隅,volatile關(guān)鍵字和顯示鎖Lock來保證可見性。

JVM通過synchronized關(guān)鍵字励七,volatile關(guān)鍵字和顯示鎖Lock來保證有序性智袭。

可見volatile關(guān)鍵字只能保證可見性和有序性。而使用鎖的話則三個(gè)特性都能保證掠抬。

volatile的使用場(chǎng)景

由于volatile關(guān)鍵字可以保證可見性和有序性吼野,并不能保證原子性,所以它不能完全替代鎖两波。我們工作中對(duì)volatile的使用也是利用了它的可見性和有序性箫锤。

  1. 修飾開關(guān)變量。比如我們上面提到的例子
  2. 修飾單例變量雨女,防止在單例模式中由于指令重排生成多個(gè)變量谚攒。

volatile關(guān)鍵字不會(huì)導(dǎo)致阻塞,但是使用鎖會(huì)導(dǎo)致阻塞

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末氛堕,一起剝皮案震驚了整個(gè)濱河市馏臭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讼稚,老刑警劉巖括儒,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異锐想,居然都是意外死亡帮寻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門赠摇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來固逗,“玉大人浅蚪,你說我怎么就攤上這事√陶郑” “怎么了惜傲?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贝攒。 經(jīng)常有香客問我盗誊,道長(zhǎng),這世上最難降的妖魔是什么隘弊? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任哈踱,我火速辦了婚禮,結(jié)果婚禮上梨熙,老公的妹妹穿的比我還像新娘嚣鄙。我一直安慰自己,他們只是感情好串结,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布哑子。 她就那樣靜靜地躺著,像睡著了一般肌割。 火紅的嫁衣襯著肌膚如雪卧蜓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天把敞,我揣著相機(jī)與錄音弥奸,去河邊找鬼。 笑死奋早,一個(gè)胖子當(dāng)著我的面吹牛盛霎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耽装,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼愤炸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了掉奄?” 一聲冷哼從身側(cè)響起规个,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姓建,沒想到半個(gè)月后诞仓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡速兔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年墅拭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涣狗。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谍婉,死狀恐怖舒憾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屡萤,我是刑警寧澤珍剑,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布掸宛,位于F島的核電站死陆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏唧瘾。R本人自食惡果不足惜措译,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饰序。 院中可真熱鬧领虹,春花似錦、人聲如沸求豫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蝠嘉。三九已至最疆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚤告,已是汗流浹背努酸。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杜恰,地道東北人获诈。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像心褐,于是被迫代替她去往敵國(guó)和親舔涎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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