深入理解Java內(nèi)存模型 - JMM簡(jiǎn)介

Java內(nèi)存模型

Java虛擬機(jī)規(guī)范中試圖定義一種Java內(nèi)存模型(Java Memory Model,簡(jiǎn)稱(chēng)JMM)來(lái)屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問(wèn)差異,以實(shí)現(xiàn)讓Java程序在各種平臺(tái)下都能達(dá)到一致的內(nèi)存訪問(wèn)效果。

定義Java內(nèi)存模型并非一件容易的事情,這個(gè)模型必須定義得足夠嚴(yán)謹(jǐn),才能讓Java的并非內(nèi)存訪問(wèn)操作不會(huì)產(chǎn)生歧義:但是,也必須定義的足夠?qū)捤?,使得虛擬機(jī)的實(shí)現(xiàn)有足夠的自由空間去利用硬件的各種特性(寄存器、高速緩存和指令集中某些特有的指令)來(lái)獲取更好的執(zhí)行速度。經(jīng)過(guò)長(zhǎng)時(shí)間的驗(yàn)證和修補(bǔ),在JDK1.5(實(shí)現(xiàn)了JSR-133)發(fā)布后,Java內(nèi)存模型已經(jīng)成熟和完善起來(lái)了。

主內(nèi)存和工作內(nèi)存

Java內(nèi)存模型的主要目標(biāo)就是定義程序中各個(gè)變量的訪問(wèn)規(guī)則,即在虛擬機(jī)中將變量存儲(chǔ)到內(nèi)存和從內(nèi)存中取出變量這樣的底層細(xì)節(jié)。此處的變量(Variables)與Java編程中所說(shuō)的變量有所區(qū)別,它包括了實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素,但不包括局部變量與方法參數(shù),因?yàn)楹笳呤蔷€程私有,不會(huì)被共享,自然就不會(huì)存在競(jìng)爭(zhēng)問(wèn)題。

Java內(nèi)存模型規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存(Main Memory)中,每個(gè)線程還有自己的工作內(nèi)存(Working Memory),線程的工作內(nèi)存中保存了被該線程使用到的變量的主內(nèi)存副本拷貝,線程對(duì)變量的所有操作(讀取、賦值等)都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫(xiě)主內(nèi)存中的變量,不同線程之間也無(wú)法直接訪問(wèn)其他線程工作內(nèi)存中的變量,線程間變量值的傳遞均需要通過(guò)主內(nèi)存來(lái)完成。線程、主內(nèi)存、工作內(nèi)存三者的交互關(guān)系如下圖所示:

Memory.png

原子性、可見(jiàn)性與有序性

Java內(nèi)存模型是圍繞著在并發(fā)過(guò)程中如何處理原子性、可見(jiàn)性與有序性這3個(gè)特征來(lái)建立,我們逐個(gè)來(lái)看一下哪些操作實(shí)現(xiàn)了這3個(gè)特性。

原子性

原子性是指一個(gè)操作或者多個(gè)操作 要么全部執(zhí)行并且執(zhí)行的過(guò)程不會(huì)被任何因素打斷,要么就都不執(zhí)行。

除了long型字段和double型字段外,Java內(nèi)存模型確保訪問(wèn)任意類(lèi)型字段所對(duì)應(yīng)的內(nèi)存單元都是原子的。這包括引用其它對(duì)象的引用類(lèi)型的字段。此外,volatile long 和volatile double也具有原子性 。

如果應(yīng)用程序需要一個(gè)更大范圍的原子性保證,Java內(nèi)存模型還提供了顯示鎖和synchronized 關(guān)鍵字。

可見(jiàn)性

可見(jiàn)性是指當(dāng)一個(gè)線程修改了共享變量的值,其他線程能夠立即得知這個(gè)修改。

Java內(nèi)存模型是通過(guò)在變量修改后將新值同步回主內(nèi)存,在變量讀取前從主內(nèi)存將刷新變量值這種依賴(lài)主內(nèi)存作為傳遞媒介的方式來(lái)實(shí)現(xiàn)可見(jiàn)性的。無(wú)論是普通變量還是volatile變量都是如此,普通變量與volatile變量的區(qū)別是:volatile的特殊規(guī)則保證了新值能立即同步到主內(nèi)存,已經(jīng)每次使用前立即從主內(nèi)存刷新。因此,volatile關(guān)鍵字可以保證多線程操作時(shí)變量的可見(jiàn)性。

除了volatile之外,Java還有兩個(gè)關(guān)鍵字能實(shí)現(xiàn)可見(jiàn)性,即synchronized和final。

有序性

有序性是指 程序執(zhí)行的順序按照代碼中的先后順序執(zhí)行。

Java語(yǔ)言提供了volatile和synchronized兩個(gè)關(guān)鍵字來(lái)保證線程之間操作的有序性。

happens-before

從JDK5開(kāi)始,java使用新的JSR -133內(nèi)存模型(本文除非特別說(shuō)明,針對(duì)的都是JSR- 133內(nèi)存模型)。JSR-133使用happens-before的概念來(lái)闡述操作之間的內(nèi)存可見(jiàn)性。在JMM中,如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見(jiàn),那么這兩個(gè)操作之間必須要存在happens-before關(guān)系。這里提到的兩個(gè)操作既可以是在一個(gè)線程之內(nèi),也可以是在不同線程之間。
與程序員密切相關(guān)的happens-before規(guī)則如下:

  • 程序順序規(guī)則:一個(gè)線程中的每個(gè)操作,happens- before 于該線程中的任意后續(xù)操作。
  • 監(jiān)視器鎖規(guī)則:對(duì)一個(gè)監(jiān)視器鎖的解鎖,happens- before 于隨后對(duì)這個(gè)監(jiān)視器鎖的加鎖。
  • volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫(xiě),happens- before 于任意后續(xù)對(duì)這個(gè)volatile域的讀。
  • 傳遞性:如果A happens- before B,且B happens- before C,那么A happens- before C。

注意,兩個(gè)操作之間具有happens-before關(guān)系,并不意味著前一個(gè)操作必須要在后一個(gè)操作之前執(zhí)行!happens-before僅僅要求前一個(gè)操作(執(zhí)行的結(jié)果)對(duì)后一個(gè)操作可見(jiàn),且前一個(gè)操作按順序排在第二個(gè)操作之前(the first is visible to and ordered before the second)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 并發(fā)系列的文章都是根據(jù)閱讀《Java 并發(fā)編程的藝術(shù)》這本書(shū)總結(jié)而來(lái),想更深入學(xué)習(xí)的同學(xué)可以自行購(gòu)買(mǎi)此書(shū)進(jìn)行學(xué)習(xí)。...
    小之丶閱讀 1,122評(píng)論 1 7
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,818評(píng)論 11 349
  • 《訴衷情》 簪花踏馬覓封侯,古道月如鉤。 西風(fēng)凜冽寒酒,欲斷暮鐘悠。 一驀起,幾回愁,上心頭。 樓蘭不破,誓守龍城...
    我那紛紛的思欲閱讀 371評(píng)論 0 3
  • 玩了一下午的手機(jī),甚是疲乏。走向陽(yáng)臺(tái),伸了個(gè)懶腰,看見(jiàn)天空的云,甚是眼熟。 還是在秋天,去年的秋天。 那時(shí)作為醫(yī)院...
    十八歲半夏閱讀 279評(píng)論 0 0
  • 這個(gè)“有效”,毫無(wú)功利色彩。作為熱愛(ài)工作、熱愛(ài)生活、熱愛(ài)孩子的二寶媽媽?zhuān)瑫r(shí)間非常緊迫,總希望同樣的時(shí)間里能帶給孩子...
    稚言之語(yǔ)閱讀 780評(píng)論 1 6

友情鏈接更多精彩內(nèi)容