volatile關(guān)鍵字

Java 內(nèi)存模型中的可見(jiàn)性础钠、原子性和有序性。

可見(jiàn)性:可見(jiàn)性叉谜,是指線程之間的可見(jiàn)性旗吁,一個(gè)線程修改的狀態(tài)對(duì)另一個(gè)線程是可見(jiàn)的。
原子性:原子是世界上的最小單位停局,具有不可分割性很钓。比如 a=0;(a非long和double類(lèi)型) 這個(gè)操作是不可分割的董栽,那么我們說(shuō)這個(gè)操作時(shí)原子操作码倦。再比如:a++; 這個(gè)操作實(shí)際是a = a + 1锭碳;是可分割的袁稽,所以他不是一個(gè)原子操作。非原子操作都會(huì)存在線程安全問(wèn)題擒抛,需要我們使用同步技術(shù)(sychronized)來(lái)讓它變成一個(gè)原子操作推汽。
有序性:Java 語(yǔ)言提供了 volatile 和 synchronized 兩個(gè)關(guān)鍵字來(lái)保證線程之間操作的有序性补疑,volatile 是因?yàn)槠浔旧戆敖怪噶钪嘏判颉钡恼Z(yǔ)義,synchronized 是由“一個(gè)變量在同一個(gè)時(shí)刻只允許一條線程對(duì)其進(jìn)行 lock 操作”這條規(guī)則獲得的歹撒,此規(guī)則決定了持有同一個(gè)對(duì)象鎖的兩個(gè)同步塊只能串行執(zhí)行莲组。

volatile的工作流程
image.png
volatile的底層原理

voliate緩存可見(jiàn)性實(shí)現(xiàn)原理:

底層實(shí)現(xiàn)主要是通過(guò)匯編lock前綴指令,它會(huì)鎖定這塊區(qū)域內(nèi)的緩存(緩存行鎖定)并會(huì)回寫(xiě)到主內(nèi)存暖夭。

IA-32架構(gòu)開(kāi)發(fā)者對(duì)lock指令的解釋?zhuān)?br> 1)會(huì)將當(dāng)前處理器緩存行的數(shù)據(jù)立即寫(xiě)會(huì)系統(tǒng)主存
2)這個(gè)寫(xiě)回內(nèi)存的操作會(huì)引起在其他cpu里緩存了該內(nèi)存地址的數(shù)據(jù)無(wú)效(MES協(xié)議)

線程2將initFlag的值store到主內(nèi)存時(shí)要通過(guò)總線锹杈,cpu總線嗅探機(jī)制監(jiān)聽(tīng)到initFlag值被修改,線程1的initFlag失效迈着,線程1需要重新read initFlag的值竭望。


image.png
volatile為什么不能保證原子性?

例如你讓一個(gè)volatile的integer自增(i++),其實(shí)要分成3步:
1)讀取volatile變量值到local寥假;
2)增加變量的值市框;
3)把local的值寫(xiě)回,讓其它的線程可見(jiàn)糕韧。這3步的jvm指令為:

mov    0xc(%r10),%r8d ; Load
inc    %r8d           ; Increment
mov    %r8d,0xc(%r10) ; Store
lock addl $0x0,(%rsp) ; StoreLoad Barrier

從Load到store到內(nèi)存屏障枫振,一共4步,其中最后一步j(luò)vm讓這個(gè)最新的變量的值在所有線程可見(jiàn)萤彩,也就是最后一步讓所有的CPU內(nèi)核都獲得了最新的值粪滤,但中間的幾步(從Load到Store)是不安全的,中間如果其他的CPU修改了值將會(huì)丟失

volatile如何實(shí)現(xiàn)禁止指令重排/有序性?

為了實(shí)現(xiàn)volatile的內(nèi)存語(yǔ)義雀扶,編譯器在生成字節(jié)碼時(shí)杖小,會(huì)在指令序列中插入“內(nèi)存屏障”來(lái)禁止特定類(lèi)型的處理器重排序。然而愚墓,對(duì)于編譯器來(lái)說(shuō)予权,發(fā)現(xiàn)一個(gè)最優(yōu)布置來(lái)最小化插入屏障的總數(shù)幾乎不可能,為此浪册,Java內(nèi)存模型采取保守策略:
在每個(gè)volatile寫(xiě)操作的前面插入一個(gè)StoreStore屏障扫腺。
在每個(gè)volatile寫(xiě)操作的后面插入一個(gè)StoreLoad屏障。
在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障村象。
在每個(gè)volatile讀操作的后面插入一個(gè)LoadStore屏障笆环。

內(nèi)存屏障是什么東西呢?

內(nèi)存屏障厚者,又稱(chēng)內(nèi)存柵欄躁劣,是一個(gè) CPU 指令。
在程序運(yùn)行時(shí)库菲,為了提高執(zhí)行性能账忘,編譯器和處理器會(huì)對(duì)指令進(jìn)行重排序,JMM 為了保證在不同的編譯器和 CPU 上有相同的結(jié)果,通過(guò)插入特定類(lèi)型的內(nèi)存屏障來(lái)禁止+ 特定類(lèi)型的編譯器重排序和處理器重排序闪萄,插入一條內(nèi)存屏障會(huì)告訴編譯器和 CPU:不管什么指令都不能和這條 Memory Barrier 指令重排序梧却。

volatile的應(yīng)用場(chǎng)景

1)定期 “發(fā)布” 觀察結(jié)果供程序內(nèi)部使用“苋ィ【例如】假設(shè)有一種環(huán)境傳感器能夠感覺(jué)環(huán)境溫度放航。一個(gè)后臺(tái)線程可能會(huì)每隔幾秒讀取一次該傳感器,并更新包含當(dāng)前文檔的 volatile 變量圆裕。然后广鳍,其他線程可以讀取這個(gè)變量,從而隨時(shí)能夠看到最新的溫度值吓妆。

2)volatile 變量的規(guī)范使用僅僅是使用一個(gè)布爾狀態(tài)標(biāo)志赊时,用于指示發(fā)生了一個(gè)重要的一次性事件,例如完成初始化或請(qǐng)求停機(jī)行拢。

3)如果讀操作遠(yuǎn)遠(yuǎn)超過(guò)寫(xiě)操作祖秒,您可以結(jié)合使用內(nèi)部鎖和 volatile 變量來(lái)減少公共代碼路徑的開(kāi)銷(xiāo)。

如下顯示的線程安全的計(jì)數(shù)器舟奠,使用 synchronized 確保增量操作是原子的竭缝,并使用 volatile 保證當(dāng)前結(jié)果的可見(jiàn)性。如果更新不頻繁的話沼瘫,該方法可實(shí)現(xiàn)更好的性能抬纸,因?yàn)樽x路徑的開(kāi)銷(xiāo)僅僅涉及 volatile 讀操作,這通常要優(yōu)于一個(gè)無(wú)競(jìng)爭(zhēng)的鎖獲取的開(kāi)銷(xiāo)耿戚。

1.  @ThreadSafe  
2.  public class CheesyCounter {  
3.  // Employs the cheap read-write lock trick  
4.  // All mutative operations MUST be done with the 'this' lock held  
5.  @GuardedBy("this") private volatile int value;  

7.  //讀操作湿故,沒(méi)有synchronized,提高性能  
8.  public int getValue() {   
9.  return value;   
10.  }   

12.  //寫(xiě)操作膜蛔,必須synchronized坛猪。因?yàn)閤++不是原子操作  
13.  public synchronized int increment() {  
14.  return value++;  
15.  }  
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市皂股,隨后出現(xiàn)的幾起案子砚哆,更是在濱河造成了極大的恐慌,老刑警劉巖屑墨,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異纷铣,居然都是意外死亡卵史,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)搜立,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)以躯,“玉大人,你說(shuō)我怎么就攤上這事∮巧瑁” “怎么了刁标?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)址晕。 經(jīng)常有香客問(wèn)我膀懈,道長(zhǎng),這世上最難降的妖魔是什么谨垃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任启搂,我火速辦了婚禮,結(jié)果婚禮上刘陶,老公的妹妹穿的比我還像新娘胳赌。我一直安慰自己,他們只是感情好匙隔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布疑苫。 她就那樣靜靜地躺著,像睡著了一般纷责。 火紅的嫁衣襯著肌膚如雪捍掺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天碰逸,我揣著相機(jī)與錄音乡小,去河邊找鬼。 笑死饵史,一個(gè)胖子當(dāng)著我的面吹牛满钟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胳喷,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼湃番,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了吭露?” 一聲冷哼從身側(cè)響起吠撮,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎讲竿,沒(méi)想到半個(gè)月后泥兰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡题禀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年鞋诗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迈嘹。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡削彬,死狀恐怖全庸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情融痛,我是刑警寧澤壶笼,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站雁刷,受9級(jí)特大地震影響覆劈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜安券,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一墩崩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侯勉,春花似錦鹦筹、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至练对,卻和暖如春遍蟋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背螟凭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工虚青, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人螺男。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓棒厘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親下隧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奢人,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359