Java內(nèi)存模型基礎(chǔ)

Java內(nèi)存模型

并發(fā)編程模型中的兩個關(guān)鍵問題

在并發(fā)編程中有兩個關(guān)鍵的問題:

  1. 線程之間如何通信
  2. 線程之間如何同步

其中通信是指線程之間以何種機制來交換信息疮胖。在命令式編程中环戈,線程之間的通信機制有兩種:共享內(nèi)存和消息傳遞闷板。Android中的Handler就是屬于消息傳遞。

在共享內(nèi)存的并發(fā)模型里院塞,線程之間共享程序的公共狀態(tài)遮晚,通過寫-讀內(nèi)存的公共狀態(tài)進行隱式通信。在消息傳遞的模型里拦止,線程沒有公共的狀態(tài)县遣,線程之間必須通過發(fā)送消息來進行通信。

同步是指程序中用于控制不同線程操作發(fā)生的相對順序的機制汹族。在共享內(nèi)存并發(fā)模型里萧求,同步是顯示進行的。必須顯示的指定某個方法或某段代碼需要在線程之間互斥執(zhí)行顶瞒。在消息傳遞的模型里夸政,由于消息的傳遞的發(fā)送必須在消息的接收之前,因此同步是隱式進行的榴徐。

Java采用的并發(fā)模型是共享內(nèi)存的模型守问。

Java內(nèi)存模型的抽象結(jié)構(gòu)

在Java中,所有的實例域坑资,靜態(tài)域和數(shù)組元素都存儲在堆內(nèi)存中耗帕,堆內(nèi)存在線程之間共享。而這些被共享的變量一般被稱為共享變量袱贮。局部變量仿便,方法定義的參數(shù)和異常處理器的參數(shù)不會在線程之間共享,它們不會有內(nèi)存可見性問題攒巍,也不受內(nèi)存模型的影響探越。

也就是說:無狀態(tài)的類是安全的。

Java內(nèi)存模型簡稱JMM窑业,Java線程之間的通信由JMM控制钦幔,JMM決定了一個線程對共享變量的寫入何時對另一個線程可見。從抽象的角度來看常柄,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:

線程的共享變量存儲在主內(nèi)存中(Main Memory)鲤氢,每一個線程都有一個私有的本地內(nèi)存(Local Memory),本地內(nèi)存中存儲了線程以讀/寫共享變量的副本西潘。本地內(nèi)存是一個JMM的抽象概念卷玉,并不真實存在。它涵蓋了緩存喷市、寫緩存區(qū)相种、寄存器以及其他的硬件和編譯器優(yōu)化。Java內(nèi)存模型的抽象示意圖如下:

java內(nèi)存模型抽象圖

如果線程A與B之間需要通信的話品姓,必須要經(jīng)歷下面的兩個步驟:

  1. 線程A把本地內(nèi)存A中更新過的共享變量刷新到主存中去寝并。
  2. 線程B到主存中去讀取線程A之前已經(jīng)更新過的共享變量箫措。
image.png

從源碼到執(zhí)行序列的重排序

在執(zhí)行程序的時候,為了提供性能衬潦,編譯器和處理器常常會對執(zhí)行做重排序斤蔓。重排序分為三種類型。

  1. 編譯器優(yōu)化重排序镀岛。編譯器在不改變單線程語言的前提下弦牡,可以重新安排語句的執(zhí)行順序。
  2. 指令級并行的重排序∑颍現(xiàn)代處理器采用了指令級并行技術(shù)(Instruction-Level Parallelism,ILP)來將多條指令重疊執(zhí)行驾锰。如果不存在數(shù)據(jù)依賴性,那么可以改變語句對應機器指令的執(zhí)行順序走越。
  3. 內(nèi)存系統(tǒng)的重排序椭豫。由于處理機使用緩存讀/寫緩沖區(qū),這使得加載和存儲操作看起來實在亂序執(zhí)行买喧。

從Java源代碼到最終執(zhí)行的指令序列捻悯,會經(jīng)過下面的3重排序匆赃。

從源碼到指令序列中的重排序

1屬于編譯器重排序淤毛,2和3屬于處理器重排序算柳。這些重排序問題可能會導致出現(xiàn)內(nèi)存可見性的問題低淡。

對于編譯器,JMM的編譯器重排序規(guī)則會禁止特定類型的編譯器重排序瞬项。對于處理器重排序囱淋,JMM的處理器重排序規(guī)則會要求Java編譯器在生成指令序列的時猪杭,插入特定的內(nèi)存屏障(Memory Barriers)指令,通過這些內(nèi)存屏障了來禁止特定類型的處理器重排序妥衣。

內(nèi)存屏障是一組計算機指令皂吮,用于實現(xiàn)對內(nèi)存操作的順序限制。

并發(fā)編程的模型分類

現(xiàn)代的處理器使用緩沖區(qū)臨時保存向內(nèi)存寫入的數(shù)據(jù)税手。寫緩存沖可以保證執(zhí)行流水線持續(xù)運行蜂筹,它可以避免由于處理器停頓下來向內(nèi)存寫入數(shù)據(jù)而產(chǎn)生的延遲。同時可以通過批處理的方法刷新緩沖區(qū)芦倒,以及合并寫緩沖區(qū)中對同一地址的多次寫艺挪,減少對內(nèi)存總線的占用。

但是有一個問題兵扬,每個處理器上的寫緩沖區(qū)僅僅對它所在的處理器可見麻裳。這個特性會對內(nèi)存操作的執(zhí)行順序產(chǎn)生影響:處理器對內(nèi)存的讀/寫操作的執(zhí)行順序口蝠,不一定與內(nèi)存發(fā)生的實際的讀寫順序一致。

為了保證可見性掂器,Java編譯器會在生成指令的適當位置會插入內(nèi)存屏障的指令來禁止特定類型的處理器重排序亚皂。JMM吧內(nèi)存屏障分為4類。

屏障類型 指令示例 說明
LoadLoad屏障 Load1; LoadLoad; Load2 在Load2及后續(xù)讀取操作要讀取的數(shù)據(jù)被訪問前国瓮,保證Load1要讀取的數(shù)據(jù)被讀取完畢灭必。
StoreStore屏障 Store1; StoreStore; Store2 在Store2及后續(xù)寫入操作執(zhí)行前,保證Store1的寫入操作對其它處理器可見乃摹。
LoadStore屏障 Load1; LoadStore; Store2 在Store2及后續(xù)寫入操作被刷出前禁漓,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
StoreLoad屏障 Store1; StoreLoad; Load2 在Load2及后續(xù)所有讀取操作執(zhí)行前孵睬,保證Store1的寫入對所有處理器可見播歼。它的開銷是四種屏障中最大的。

StoreLoad屏障是一個“全能型”的屏障掰读。它同時具有其他三個屏障的效果秘狞。

happens-before簡介

從jdk1.5開始,Java使用新的JSR-133內(nèi)存模型蹈集。JSR-133使用happens-before的概念來闡述操作之間的內(nèi)存可見性烁试。在JVM中,如果一個操作執(zhí)行的結(jié)果需要對另一個操作可見拢肆,那么這兩個操作操作之間必須存在happens-before關(guān)系减响,這里提到的兩個操作既可以是在一個線程之內(nèi),也可以是在不同線程之間郭怪。

happens-before規(guī)則如下:

1支示、程序次序規(guī)則:在一個單獨的線程中,按照程序代碼的執(zhí)行流順序鄙才,(時間上)先執(zhí)行的操作happen—before(時間上)后執(zhí)行的操作颂鸿。

2、管理鎖定規(guī)則:一個unlock操作happen—before后面(時間上的先后順序攒庵,下同)對同一個鎖的lock操作嘴纺。

3、volatile變量規(guī)則:對一個volatile變量的寫操作happen—before后面對該變量的讀操作叙甸。

4颖医、線程啟動規(guī)則:Thread對象的start()方法happen—before此線程的每一個動作。

5裆蒸、線程終止規(guī)則:線程的所有操作都happen—before對此線程的終止檢測熔萧,可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值等手段檢測到線程已經(jīng)終止執(zhí)行。

6佛致、線程中斷規(guī)則:對線程interrupt()方法的調(diào)用happen—before發(fā)生于被中斷線程的代碼檢測到中斷時事件的發(fā)生贮缕。

7、對象終結(jié)規(guī)則:一個對象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)happen—before它的finalize()方法的開始俺榆。

8感昼、傳遞性:如果操作A happen—before操作B,操作B happen—before操作C罐脊,那么可以得出A happen—before操作C定嗓。

一個happens-before規(guī)則對應與一個或多個編譯器和處理器重排序規(guī)則。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末萍桌,一起剝皮案震驚了整個濱河市宵溅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌上炎,老刑警劉巖恃逻,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異藕施,居然都是意外死亡寇损,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門裳食,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矛市,“玉大人,你說我怎么就攤上這事胞谈〕九危” “怎么了憨愉?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵烦绳,是天一觀的道長。 經(jīng)常有香客問我配紫,道長径密,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任躺孝,我火速辦了婚禮享扔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘植袍。我一直安慰自己惧眠,他們只是感情好,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布于个。 她就那樣靜靜地躺著氛魁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秀存,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天捶码,我揣著相機與錄音,去河邊找鬼或链。 笑死惫恼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的澳盐。 我是一名探鬼主播祈纯,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叼耙!你這毒婦竟也來了盆繁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤旬蟋,失蹤者是張志新(化名)和其女友劉穎油昂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倾贰,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡冕碟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匆浙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片安寺。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖首尼,靈堂內(nèi)的尸體忽然破棺而出挑庶,到底是詐尸還是另有隱情,我是刑警寧澤软能,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布迎捺,位于F島的核電站,受9級特大地震影響查排,放射性物質(zhì)發(fā)生泄漏凳枝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一跋核、第九天 我趴在偏房一處隱蔽的房頂上張望岖瑰。 院中可真熱鬧,春花似錦砂代、人聲如沸蹋订。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽露戒。三九已至难礼,卻和暖如春汽煮,著一層夾襖步出監(jiān)牢的瞬間冶忱,已是汗流浹背肛搬。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工校翔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褐奴,地道東北人浪读。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓比吭,卻偏偏與公主長得像黍析,于是被迫代替她去往敵國和親节沦。 傳聞我的和親對象是個殘疾皇子键思,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

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