java多線程之三——volatile

在多線程編程中粘昨,Synchronized 和 volatile 都扮演者重要的角色,前面的文章我們已經了解了java內置鎖Synchronized 盗迟,它保證了并發(fā)過程中的可見性與原子性,避免了共享數(shù)據(jù)的錯誤。
而 Volatile可以看做是輕量級的 Synchronized厌漂,它只保證了共享變量的可見性。在線程 A 修改了被 volatile 修飾的共享變量之后斟珊,線程 B 能夠讀取到正確的值苇倡。在 關于JMM 的文章中我們了解到 java 在多線程中操作共享變量的過程中,會存在指令重排序與共享變量工作內存緩存的問題囤踩。

volatile作為一個修飾符旨椒,使用很簡單,但是它背后做了多少工作呢堵漱?

首先我們需要明白综慎,本地內存是一個抽象概念,包括緩存勤庐、讀寫緩沖區(qū)示惊、寄存器好港,甚至編譯器重排序和cpu重排序。JVM按照JMM規(guī)范對volatile進行特殊處理米罚,從而實現(xiàn)在CPU對該變量的特殊處理钧汹。

volatile底層原理

計算機系統(tǒng)中,硬盤負責存儲數(shù)據(jù)录择, 但是數(shù)據(jù)交換速度慢拔莱,CPU 運行速度非常快隘竭,CPU直接硬盤的數(shù)據(jù)交換效率非常低塘秦,于是產生了內存,通過內存與 CPU 進行數(shù)據(jù)交換动看,但是內存的速度依舊不夠快嗤形,嚴重拖慢整體的運行效率,故而在 CPU 內部添加了高速緩存弧圆,作為 CPU的臨時存儲器赋兵,與內存的數(shù)據(jù)交互。

  • 在單核CPU中搔预,多線程都在一個CPU中進行運行霹期,共用一份緩存,對同一個共享變量的使用拯田,而不會出現(xiàn)數(shù)據(jù)可見性的問題
  • 而多核CPU由于多線程可能分配在不同的CPU历造,這種情況下進行計算時,就會出現(xiàn)一個CPU內核計算完成船庇,并沒有同步回主內存吭产,而其他CPU無法使用最新的數(shù)據(jù),而出現(xiàn)了可見性問題鸭轮。

通過添加volatile修飾臣淤,通過JVM的優(yōu)化,最后反應到CPU上窃爷,先從內存獲取數(shù)據(jù)邑蒋,存儲在高速緩存中,然后再從高速緩存中獲取數(shù)據(jù)進行計算按厘,計算完成后的值并不會立即刷新回主內存中医吊,而其他 CPU 這時并不知道變量值已經改變,使用的還是之前的變量值進行計算逮京,這就產生了數(shù)據(jù)錯誤卿堂。這種機制類似我們之前講過的 JMM 中主內存于工作內存的關系。

我們知道懒棉,javac 編譯器將 .java 代碼編譯成為 .class 字節(jié)碼草描,JVM 通過解釋器與即時編譯器(JIT)運行字節(jié)碼中的指令览绿,將字節(jié)碼指令翻譯稱為具體的機器碼指令,而被 volatile 修飾的共享變量陶珠,在翻譯成為機器碼的過程中為其賦值操作添加特殊機器碼指令前綴Lock xxxx

public class Test{
  private volatile  int i=1;//被 volatile 修飾

  //線程A修改
  public void setVar(){
        i=2;
  }

 //線程B獲取
  public int getVar(){
       return i享钞;
  }
}

在執(zhí)行此條指令時揍诽,Lock 指令有兩個作用:

  • 使本CPU的緩存寫入內存
  • 上面的寫入動作也會引起別的CPU或別的內核中的緩存無效,

所以通過這樣一個指令前綴栗竖,可以讓對volatile變量的修改對其他CPU可見暑脆。

指令重排序

還是上文中的Lock前綴的作用,為什么它能禁止指令重排序呢狐肢?

從JMM角度講:

在JMM的邏輯實現(xiàn)中添吗,當操作一個變量 執(zhí)行putfield指令(為變量賦值) 時,JVM會檢查此變量是否是被volatile修飾的份名,如果是的話碟联,JVM會為該變量添加內存屏障,用于隔離該變量與前后操作僵腺,從而禁止volatile變量的操作與前后操作的亂序鲤孵。

摘自java并發(fā)編程的藝術:
為了實現(xiàn)volatile的內存語義,編譯器在生成字節(jié)碼時辰如,會在指令序列中插入內存屏障來
禁止特定類型的處理器重排序普监。對于編譯器來說,發(fā)現(xiàn)一個最優(yōu)布置來最小化插入屏障的總
數(shù)幾乎不可能琉兜。為此凯正,JMM采取保守策略。下面是基于保守策略的JMM內存屏障插入策略豌蟋。
·在每個volatile寫操作的前面插入一個StoreStore屏障廊散。
·在每個volatile寫操作的后面插入一個StoreLoad屏障。
·在每個volatile讀操作的后面插入一個LoadLoad屏障梧疲。
·在每個volatile讀操作的后面插入一個LoadStore屏障奸汇。


image.png

從CPU執(zhí)行角度講:

以上的內存屏障就會在執(zhí)行時生成相應帶有Lock前綴的機器碼(全面已提及)。在CPU中往声,程序的執(zhí)行計算是由CPU在不影響邏輯結果的前提下分配給不同的電路去處理邏輯擂找,Lock指令前綴刷新回內存,必然是在此指令之前的運算全部計算完成之后浩销,取得正確的結果才會刷新回內存的贯涎,所以這也形成了一道內存屏障,表示對該變量操作之前的操作不會亂序到其后慢洋,其后的操作不會亂序到之前塘雳。

綜上述陆盘,volatile的實現(xiàn)就是一個Lock指令前綴的作用。

使用注意事項

volatile雖然保證了可見性败明,但是它不保證原子性隘马。

諸如i++之類的語句,在執(zhí)行時的步驟:

  1. 從內存取值妻顶,放到CPU緩存中
  2. CPU中i+1
  3. 存在緩存中
  4. 刷新會內存

可見這這并不是單純的賦值操作酸员,而是有在第4步完成之前,其他CPU內核是看不到數(shù)值變化的讳嘱,而如果僅用volatile修飾的話幔嗦,僅僅保證了第3部完成之后,會立即刷新回內存沥潭,但不會保證第2步計算與第3邀泉,4步的原子性。如果線程A計算+1之后钝鸽,沒有刷回內存汇恤,線程B也+1,那么最后的結果肯定是比期望的結果小的拔恰。所以在多線程操作++時屁置,還是應該使用synchronized等同步操作保證原子性。

volatile比synchronized輕量仁连,只保證可見性蓝角。正因如此,在java.util.concurrent中AQS使用了被volatile修飾的變量來標記狀態(tài)饭冬,實現(xiàn)了靈活多樣的各種鎖使鹅,補充了內置鎖synchronized的互斥等缺點。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末昌抠,一起剝皮案震驚了整個濱河市患朱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炊苫,老刑警劉巖裁厅,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異侨艾,居然都是意外死亡执虹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門唠梨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袋励,“玉大人,你說我怎么就攤上這事〔绻剩” “怎么了盖灸?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長磺芭。 經常有香客問我赁炎,道長,這世上最難降的妖魔是什么钾腺? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任徙垫,我火速辦了婚禮,結果婚禮上垮庐,老公的妹妹穿的比我還像新娘松邪。我一直安慰自己坞琴,他們只是感情好哨查,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著剧辐,像睡著了一般寒亥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荧关,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天溉奕,我揣著相機與錄音,去河邊找鬼忍啤。 笑死加勤,一個胖子當著我的面吹牛,可吹牛的內容都是我干的同波。 我是一名探鬼主播鳄梅,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼未檩!你這毒婦竟也來了戴尸?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤冤狡,失蹤者是張志新(化名)和其女友劉穎孙蒙,沒想到半個月后卢佣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泪电,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年搜吧,在試婚紗的時候發(fā)現(xiàn)自己被綠了合瓢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浑测。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出迁央,到底是詐尸還是另有隱情掷匠,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布岖圈,位于F島的核電站讹语,受9級特大地震影響,放射性物質發(fā)生泄漏蜂科。R本人自食惡果不足惜顽决,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望导匣。 院中可真熱鬧才菠,春花似錦、人聲如沸贡定。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缓待。三九已至蚓耽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旋炒,已是汗流浹背步悠。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瘫镇,地道東北人鼎兽。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像铣除,于是被迫代替她去往敵國和親谚咬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容