Java內(nèi)存模型(JMM)

JMM定義

Java內(nèi)存模型(Java Memory Model,簡稱JMM)嘱朽,JMM為Java虛擬機(jī)規(guī)范中定義的虛擬模型旭贬,用來屏蔽各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,主要目標(biāo)是定義程序中各個變量的訪問規(guī)則搪泳,即在虛擬機(jī)中將變量存儲到內(nèi)存和從內(nèi)存中取出變量這樣底層細(xì)節(jié)稀轨。此處的變量與Java編程時所說的變量不一樣,指包括了實(shí)例字段岸军、靜態(tài)字段和構(gòu)成數(shù)組對象的元素奋刽,但是不包括局部變量與方法參數(shù),后者是線程私有的艰赞,不會被共享佣谐,不存在競爭問題。
Java內(nèi)存模型中規(guī)定:

  • 線程對變量的所有操作(讀取方妖、賦值)都必須在工作內(nèi)存中進(jìn)行狭魂,而不能直接讀寫主內(nèi)存中的變量
  • 不同線程之間無法直接訪問對方工作內(nèi)存中的變量,線程間變量值的傳遞均需要在主內(nèi)存來完成


    線程、工作內(nèi)存雌澄、主內(nèi)存交互關(guān)系

    線程的工作內(nèi)存中保存了被該線程使用到的變量的主內(nèi)存副本拷貝(不會拷貝整個對象斋泄,只會拷貝線程訪問到的內(nèi)容)

內(nèi)存間交互操作

主內(nèi)存和工作內(nèi)存的交互協(xié)議,Java內(nèi)存模型中定義了8中操作镐牺,虛擬機(jī)必須保證每種操作都是原子的炫掐、不可再分的(即最小單元)(對于double、long類型睬涧,load募胃、store、read和write在某些平臺允許例外)


JMM八種原子性操作

上面的八種操作具有一定的執(zhí)行規(guī)則:

  • 不允許 read 和 load畦浓、 store 和 write 操作之一單獨(dú)出現(xiàn)痹束, 即不允許一個變量從主內(nèi)存讀取了但工作內(nèi)存不接受, 或者從工作內(nèi)存發(fā)起回寫了但主內(nèi)存不接受的情況出現(xiàn)讶请。(必須先執(zhí)行read再執(zhí)行l(wèi)oad参袱,先執(zhí)行store在執(zhí)行write,不必保證連續(xù)執(zhí)行
  • 不允許一個線程丟棄它的最近的 assign 操作秽梅, 即變量在工作內(nèi)存中改變了之后必須把該變化同步回主內(nèi)存抹蚀。(assign-->store-->write:每次更新完成立刻寫會主內(nèi)存,保證其他線程可見
  • 不允許一個線程無原因地(沒有發(fā)生過任何 assign 操作)把數(shù)據(jù)從線程的工作內(nèi)存中同步回主內(nèi)存中企垦。
  • 一個新的變量只能在主內(nèi)存中 “ 誕生 “环壤,不允許在工作內(nèi)存中直接使用一個未被初始化 (load 或 assign) 的變量,對一個變量實(shí)施use钞诡、store操作之前郑现,必須先執(zhí)行過了load和assign操作。(read-->load-->use:每次使用都重新加載
  • 一個變量在同一時刻只允許一條線程對其進(jìn)行l(wèi)ock操作荧降,但lock操作可以被同一條線程執(zhí)行多次接箫,只有執(zhí)行相同次數(shù)的unlock,變量才會被解鎖朵诫。
  • 如果對變量執(zhí)行l(wèi)ock操作辛友,將會清空工作內(nèi)存中該變量的值,在執(zhí)行引擎使用這個變量前剪返,重新執(zhí)行 load或assign操作初始化該變量的值废累。(保證了變量的線程同步)
  • 如果沒有l(wèi)ock,就不允許unlock操作脱盲,同時也不允許unlock其他線程鎖定的變量邑滨。
  • 對變量執(zhí)行unlock之前,必須把此變量同步回主內(nèi)存中(store钱反、write操作)掖看。

對于long和double類型的特殊規(guī)則

Java對于long和double在模型中定義了比較寬松的規(guī)定:允許虛擬機(jī)將沒有被volatile修飾的64位數(shù)據(jù)的讀寫分為兩次操作進(jìn)行匣距,即允許虛擬機(jī)實(shí)現(xiàn)選擇可以不保證64位數(shù)據(jù)類型的load、store哎壳、read墨礁、write這4個操作的原子性,即long和double的非原子性協(xié)定耳峦。
多線程情況下,可能存在讀到“半個變量”的情況焕毫。但是在實(shí)際的商用虛擬機(jī)中蹲坷,幾乎都選擇把64位的讀寫操作作為原子操作來對待,所以一般情況下不用將long邑飒、double設(shè)置為volatile變量循签。

原子性、可見性疙咸、有序性

JMM圍繞著在并發(fā)過程中如何處理可見性县匠、原子性、有序性這三個特性而建立的模型撒轮。

原子性(Atomicity)

什么是原子性呢乞旦?就是這個操作要么全部做完,要么全部不做题山,不允許做一半兰粉。舉例說明:

a = a + 1;

結(jié)合線程、工作內(nèi)存顶瞳、主內(nèi)存交互關(guān)系圖玖姑,我們知道這段代碼會執(zhí)行以下三個步驟:

  1. 將a的值從主存拿到線程的工作內(nèi)存中;
  2. 在工作內(nèi)存中執(zhí)行 +1 操作得到新的結(jié)果慨菱;
  3. 將工作內(nèi)存中新的結(jié)果寫回主存焰络。

這三個操作分別具有原子性,CPU完全可能在執(zhí)行完其中的某個步驟后去執(zhí)行其他的內(nèi)容(比如線程A執(zhí)行完步驟2符喝,線程B也執(zhí)行了這段代碼且執(zhí)行完成闪彼,A再執(zhí)行步驟3,會覆蓋掉線程B的結(jié)果)协饲,這就可能導(dǎo)致多線程并發(fā)情況下的安全問題备蚓。如果沒有g(shù)et到原子性的重要性,下面看個接地氣的生活“實(shí)例”:
街邊買個煎餅囱稽,掃碼付錢10元郊尝,包含了兩個步驟:你的賬戶扣10元,店家賬戶增加10元战惊,如果整個操作不滿足原子性流昏,你的賬戶扣了錢但店家沒有收到,那店家能放你走么?如果滿足原子性况凉,你的賬戶扣了錢谚鄙,店家賬戶也收到了錢,歡迎下次光臨刁绒。
JMM直接保證的原子性變量操作包括read闷营、load、assign知市、use傻盟、store和write,我們可以大致認(rèn)為基本數(shù)據(jù)類型的訪問讀寫是具備原子性的(long和double具有非原子性協(xié)定)嫂丙。但在我們?nèi)粘i_發(fā)中娘赴,我們需要大范圍的原子性保證(例如整個業(yè)務(wù)方法),Java還提供了lock跟啤、unlock诽表,這兩個操作并沒有直接交給用戶使用,利用字節(jié)碼指令monitorenter隅肥、monitorexit來隱式使用這兩個操作竿奏,這兩個字節(jié)碼反映到Java中就是同步塊synchronized關(guān)鍵字,即synchronized可以保證其修飾的代碼塊的原子性腥放。

可見性

可見性是指當(dāng)一個線程修改了共享變量的值议双,其他線程能夠立即得知這個修改。在線程捉片、工作內(nèi)存平痰、主內(nèi)存交互關(guān)系一圖中,我們可以知道Java內(nèi)存模型是通過在工作內(nèi)存修改值后刷新到主存伍纫,讀取時從主存刷新到工作內(nèi)存這種方式來實(shí)現(xiàn)線程間的通信宗雇,無論是volatile關(guān)鍵詞修飾的變量還是普通變量都是采用的這種方式,但volatile變量和普通變量的區(qū)別是:volatile采用內(nèi)存屏障保證了變量的可見性(內(nèi)存屏障(Memory Barrier))莹规,普通變量無法保證可見性赔蒲。synchronized和final也可以保證可見性,synchronized由“對變量執(zhí)行unlock之前良漱,必須把此變量同步回主內(nèi)存中(store舞虱、write操作)”保證;final的可見性指:被final修飾的字段在構(gòu)造器中初始化完成母市,且構(gòu)造器沒有把“this”引用傳遞出去(this引用逃逸矾兜?這個不太懂),那在其他線程中就可以看到final字段的值患久。

有序性

Java程序天然的有序性可以總結(jié)為:如果在本線程內(nèi)觀察椅寺,所有的操作均是有序的(線程內(nèi)表現(xiàn)為串行的語義 As-If-Serial)浑槽;如果在一個線程內(nèi)觀察另一個線程,所有的操作都是無序的(指令重排序返帕、工作內(nèi)存與主存同步延遲)桐玻。

參考文章

文中部分內(nèi)容摘自《深入理解Java虛擬機(jī)》一書,這本書講得很好荆萤,每次看都有不同的體會镊靴。文章作為個人筆記、總結(jié)链韭,如有錯誤偏竟、不合適的內(nèi)容,留言指正梧油。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市州邢,隨后出現(xiàn)的幾起案子儡陨,更是在濱河造成了極大的恐慌,老刑警劉巖量淌,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骗村,死亡現(xiàn)場離奇詭異,居然都是意外死亡呀枢,警方通過查閱死者的電腦和手機(jī)胚股,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裙秋,“玉大人琅拌,你說我怎么就攤上這事≌蹋” “怎么了进宝?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長枷恕。 經(jīng)常有香客問我党晋,道長,這世上最難降的妖魔是什么徐块? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任未玻,我火速辦了婚禮,結(jié)果婚禮上胡控,老公的妹妹穿的比我還像新娘扳剿。我一直安慰自己,他們只是感情好昼激,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布舞终。 她就那樣靜靜地躺著轻庆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敛劝。 梳的紋絲不亂的頭發(fā)上余爆,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音夸盟,去河邊找鬼蛾方。 笑死,一個胖子當(dāng)著我的面吹牛上陕,可吹牛的內(nèi)容都是我干的桩砰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼释簿,長吁一口氣:“原來是場噩夢啊……” “哼亚隅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起庶溶,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤煮纵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后偏螺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體行疏,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年套像,在試婚紗的時候發(fā)現(xiàn)自己被綠了酿联。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡夺巩,死狀恐怖贞让,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柳譬,我是刑警寧澤震桶,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站征绎,受9級特大地震影響蹲姐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜人柿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一柴墩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凫岖,春花似錦江咳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爹土。三九已至,卻和暖如春踩身,著一層夾襖步出監(jiān)牢的瞬間胀茵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工挟阻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留琼娘,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓附鸽,卻偏偏與公主長得像脱拼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坷备,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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

  • JMM簡介 Java的內(nèi)存模型JMM(Java MemoryModel)JMM主要是為了規(guī)定了線程和內(nèi)存之間的一些...
    團(tuán)長plus閱讀 1,242評論 0 2
  • Java內(nèi)存模型(JMM) 我們常說的JVM內(nèi)存模式指的是JVM的內(nèi)存分區(qū)熄浓;而Java內(nèi)存模式是一種虛擬機(jī)規(guī)范。 ...
    西華子閱讀 591評論 0 4
  • 隨著計算機(jī)系統(tǒng)的發(fā)展省撑,多任務(wù)處理器系統(tǒng)在現(xiàn)代計算機(jī)操作系統(tǒng)中已經(jīng)是一個必不可少的組成部分了赌蔑。在很多情況下,如果想要...
    LeonardoEzio閱讀 990評論 0 1
  • Java內(nèi)存區(qū)域 Java虛擬機(jī)在運(yùn)行程序時會把其自動管理的內(nèi)存劃分為以上幾個區(qū)域丁侄,每個區(qū)域都有的用途以及創(chuàng)建銷毀...
    架構(gòu)師springboot閱讀 1,762評論 0 5
  • 預(yù)警 本文約4千字惯雳,預(yù)計花費(fèi)15分鐘閱讀完~~ 本文內(nèi)容比較枯燥朝巫,但如果你能認(rèn)認(rèn)真真的看完鸿摇,那你對Java內(nèi)存模型...
    oneape15閱讀 1,115評論 0 7