保證可見性 之 禁止重排序

所屬文集:一起掌握并發(fā)

了解重排序

為了性能蹦误,編譯時和運(yùn)行時都會有重排序咱筛,造成指令執(zhí)行順序變了,宏觀上從這3點(diǎn)了解重排序:

  1. 線程內(nèi)有序:如果再本線程內(nèi)觀察嫌佑,所有的操作都是有序的豆茫,即線程內(nèi)表現(xiàn)為串行的語義(Within Thread As-If-Serial Semantics)。
  2. 線程間無序:如果再一個線程中觀察另一個線程歧强,所有的操作都是無序的澜薄,即 指指令重排序現(xiàn)象和工作內(nèi)存與主內(nèi)存同步延遲現(xiàn)象为肮。
  3. 總會有重排序:指令重排序在任何時候都有可能發(fā)生摊册,與是否為多線程無關(guān),之所以在單線程下感覺沒有發(fā)生重排序颊艳,是因?yàn)榫€程內(nèi)表現(xiàn)為串行的語義的存在茅特。
引起可見性問題的病因

修復(fù)Java內(nèi)存模型忘分,第2部分里的一段話開始思考

理解JMM所需的關(guān)鍵概念之一是可見性 -您如何知道如果線程A執(zhí)行someVariable = 3,其他線程將看到線程A在其中 寫入的值3白修?存在許多原因妒峦,為什么另一個線程可能不會立即為以下值看到值3 someVariable:可能是因?yàn)榫幾g器已對指令進(jìn)行了重新排序以便更有效地執(zhí)行,或者已將someVariable其緩存在寄存器中兵睛,或者將其值寫入了緩存在寫處理器上但尚未刷新到主內(nèi)存肯骇,或者在讀處理器的緩存中有舊的(或陳舊的)值。內(nèi)存模型確定線程何時可以可靠地“看到”對其他線程所做的變量的寫入祖很。特別是笛丙,內(nèi)存模型為定義了語義volatile,synchronized假颇,final這確保了跨線程的內(nèi)存操作可見性胚鸯。

總結(jié)為:導(dǎo)致可見性的原因有很多

  1. 為了提升性能而實(shí)施的編譯期重排序。
  2. 數(shù)據(jù)在寄存器中笨鸡。
  3. cpu緩存的更改未同步到主內(nèi)存中 或 內(nèi)存中的更改未同步到cpu緩存(運(yùn)行期重排序)姜钳。
  4. 。形耗。哥桥。

這里兩次提到重排序,那就先看重排序激涤。

重排序 遵守 as-if-serial語義

as-if-serial語義 : 不管怎么重排序(編譯器和處理器為了提高并行度)泰讽,程序在單線程中的執(zhí)行結(jié)果不會改變。

編譯器昔期、runtime和處理器都必須遵守as-if-serial語義:

  1. 為了遵守as-if-serial語義已卸,編譯器和處理器不會對存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因?yàn)檫@種重排序會改變執(zhí)行結(jié)果硼一。

  2. 如果操作之間不存在數(shù)據(jù)依賴關(guān)系累澡,這些操作就可能被編譯器和處理器重排序

正確的理解as-if-serial語義的功效:

  1. 如果再本線程內(nèi)觀察,所有的操作都是有序的般贼;
  2. 如果再一個線程中觀察另一個線程愧哟,所有的操作都是無序的。
重排序的類型:

在執(zhí)行程序時為了提高性能哼蛆,編譯器和處理器常常會對指令做重排序蕊梧。重排序分三種類型:

  1. 編譯器優(yōu)化的重排序。編譯器在不改變單線程程序語義的前提下腮介,可以重新安排語句的執(zhí)行順序肥矢。屬于編譯期重排序。

  2. 指令級并行的重排序〉矗現(xiàn)代處理器采用了指令級并行技術(shù)(Instruction-Level Parallelism甘改, ILP)來將多條指令重疊執(zhí)行旅东。如果不存在數(shù)據(jù)依賴性,處理器可以改變語句對應(yīng)機(jī)器指令的執(zhí)行順序十艾。屬于運(yùn)行期重排序抵代。

  3. 內(nèi)存系統(tǒng)的重排序。由于處理器使用緩存和讀 / 寫緩沖區(qū)忘嫉,這使得加載和存儲操作看上去可能是在亂序執(zhí)行荤牍,屬于運(yùn)行期重排序。

image.png

從編譯運(yùn)行視角分為兩類:

  1. 編譯期重排序:包括 編譯器優(yōu)化的重排序庆冕。
  2. 運(yùn)行期重排序:包括 指令級并行的重排序参淫,內(nèi)存系統(tǒng)的重排序。

理解什么叫運(yùn)行期重排序

image.png

解讀上圖:已變更的數(shù)據(jù)立即寫到內(nèi)存太慢愧杯,所以先寫到Store Buffer.
舉個例子涎才,廚師飯做好了,不會直接端給你力九,而是放到那里等服務(wù)員端給你耍铜,無論服務(wù)員什么時候端給你,都不算是做好了直接給你吃跌前,而是放置了一會兒棕兼。

image.png

解讀上圖:其他cpu通知說,我緩存的數(shù)據(jù)無效了抵乓,但是我在忙別的伴挚,不想打斷正在做的事情,于是提供了一個通知隊(duì)列灾炭,讓他們把緩存無效的通知先放到 通知隊(duì)列中茎芋,等我忙完了再去處理通知

store buffer

  1. 優(yōu)點(diǎn):
  • 可以保證core內(nèi)的指令流水線持續(xù)運(yùn)行,
  • 它可以避免由于處理器停頓下來等待向內(nèi)存寫入數(shù)據(jù)而產(chǎn)生的延遲蜈出。
  • 通過以批處理的方式刷新寫緩沖區(qū)田弥,以及合并寫緩沖區(qū)中對同一內(nèi)存地址的多次寫,可以減少對內(nèi)存總線的占用铡原。
  1. 缺點(diǎn):
    每個處理器上的寫緩沖區(qū)偷厦,僅對它所在的處理器可見。這個特性會對內(nèi)存操作的執(zhí)行順序產(chǎn)生重要的影響:處理器對內(nèi)存的寫操作的執(zhí)行順序燕刻,不一定與內(nèi)存實(shí)際發(fā)生的寫操作順序一致只泼!

Invalidate Queue

  1. 優(yōu)點(diǎn):
    正在處理的事情不中斷
  2. 缺點(diǎn):
    處理器對內(nèi)存的讀操作的執(zhí)行順序,不一定與內(nèi)存實(shí)際發(fā)生的寫操作順序一致卵洗!使用已經(jīng)過期的數(shù)據(jù)请唱,而不是最新的。

在兩個CPU同時運(yùn)行的情況下,CPU0自身視角來說籍滴,沒有重排發(fā)生酪夷,一切都那么自然榴啸,但是CPU1卻看到CPU0發(fā)生了重排(reordering memory)孽惰。這就是內(nèi)存系統(tǒng)重排序。

禁止運(yùn)行期重排序

store bufferInvalidate Queue 帶來的亂序如何解決

CPU通常提供了內(nèi)存屏障指令鸥印,來解決這樣的亂序問題勋功。讀屏障,清空本地的invalidate queue库说,保證之前的所有l(wèi)oad都已經(jīng)生效狂鞋;寫屏障,清空本地的store buffer潜的,使得之前的所有store操作都生效骚揍。

通俗來說就是兩點(diǎn):

寫屏障:保證把更新寫到內(nèi)存
讀屏障:保證從內(nèi)存讀取最新數(shù)據(jù)

JMM把內(nèi)存屏障分為四類,其實(shí)就是讀啰挪、寫屏障的組合:

image.png

JMM 的處理器重排序規(guī)則 會 要求 java 編譯器在生成指令序列時信不,插入特定類型的內(nèi)存屏障指令,來禁止特定類型的處理器重排序(不是所有的處理器重排序都要禁止)亡呵。

理解什么叫編譯期重排序:
private int val = 0;
private volatile boolean stop = false;
...
fun(){
val = 10;//代碼1
stop = true;//代碼2
}

這里代碼1 和代碼2 編譯后順序不會調(diào)整很容易理解抽活,但是下邊這個例子就有歧義了:

private boolean stop ;
while(!stop){
  i++;
}

按照java中volatile關(guān)鍵字的疑惑大濕的解答
這種代碼會被編譯優(yōu)化成類似

if(!stop){
     while(true){
          i++;
    }
}

這種答案我還是不太信服的,看官您若有答案請留言告知哦锰什。
大師的答案中用到了HSDIS技術(shù)下硕,深入解析volatile關(guān)鍵字 對HSDIS的講解很全面

禁止編譯期重排序

JMM 的編譯器重排序規(guī)則 會 禁止 特定類型的編譯器重排序(不是所有的編譯器重排序都要禁止)。

java中禁止重排序的操作

  1. volatile關(guān)鍵字

  2. unsafe 的內(nèi)存屏障方法

  3. synchronized鎖

題外話之總線風(fēng)暴

總線風(fēng)暴:總線帶寬達(dá)到峰值汁胆;原因:

  1. 內(nèi)存屏障從主內(nèi)存嗅探
  2. cas不斷循環(huán)無效交互導(dǎo)致

解決辦法:
部分volatile和cas使用synchronize

Happens-Before 規(guī)則 關(guān)注使用效果而非實(shí)現(xiàn)梭姓。

上述內(nèi)容應(yīng)該也只是保證可見性的一部分內(nèi)容,我本身還困惑寄存器緩存和指令并行重排等情況嫩码,作為上層高級語言程序員糊昙,很難很難掌握全部底層的情況。

通過一種更簡單的方式谢谦,結(jié)合 java語法释牺,以使用的視角來掌握如何保證可見性,而不是 如何實(shí)現(xiàn)可見性保證回挽。很自然的happens-before的概念就是這個作用没咙,告訴程序員怎么用。 而保證可見性的實(shí)現(xiàn)則是JMM自己的事情千劈,不是程序員的祭刚。

image.png

從 JDK5 開始 java 使用新的 JSR -133 內(nèi)存模型,并依據(jù)此內(nèi)存模型提出了 happens-before 的概念,通過這個概念來闡述操作之間的內(nèi)存可見性涡驮。

我們要保證可見性暗甥,就是遵守Happens-Before 規(guī)則,合理的使用java提供的工具捉捅。


為什么會有內(nèi)存屏障

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撤防,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子棒口,更是在濱河造成了極大的恐慌寄月,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件无牵,死亡現(xiàn)場離奇詭異漾肮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)茎毁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門克懊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人七蜘,你說我怎么就攤上這事谭溉。” “怎么了崔梗?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵夜只,是天一觀的道長。 經(jīng)常有香客問我蒜魄,道長扔亥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任谈为,我火速辦了婚禮旅挤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伞鲫。我一直安慰自己粘茄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布秕脓。 她就那樣靜靜地躺著柒瓣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吠架。 梳的紋絲不亂的頭發(fā)上尖殃,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天帚屉,我揣著相機(jī)與錄音,去河邊找鬼再来。 笑死遏佣,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼擦酌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了菠劝?” 一聲冷哼從身側(cè)響起赊舶,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闸英,沒想到半個月后锯岖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體介袜,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甫何,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了遇伞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辙喂。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鸠珠,靈堂內(nèi)的尸體忽然破棺而出巍耗,到底是詐尸還是另有隱情,我是刑警寧澤渐排,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布炬太,位于F島的核電站,受9級特大地震影響驯耻,放射性物質(zhì)發(fā)生泄漏亲族。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一可缚、第九天 我趴在偏房一處隱蔽的房頂上張望霎迫。 院中可真熱鬧,春花似錦帘靡、人聲如沸知给。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涩赢。三九已至,卻和暖如春轩勘,著一層夾襖步出監(jiān)牢的瞬間筒扒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工赃阀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霎肯,地道東北人擎颖。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像观游,于是被迫代替她去往敵國和親搂捧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

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