一导披、現(xiàn)代計算機(jī)物理內(nèi)存模型
Java內(nèi)存模型規(guī)定了JVM如何基于計算機(jī)內(nèi)存工作梗劫。JVM就是一個完整的計算機(jī)模型因此這個模型中包含一個內(nèi)存模型
現(xiàn)代計算機(jī)的內(nèi)存與處理器的運(yùn)算能力之間有幾個數(shù)量級的差距蝌借,所以現(xiàn)代計算機(jī)系統(tǒng)加入讀寫速度盡可能接近處理器運(yùn)算速度的高速緩存(L1匣距、L2、L3三級緩存容量依此增大,速度依此降低)來作為內(nèi)存與處理器之間的緩沖:將運(yùn)算需要使用到的數(shù)據(jù)復(fù)制到緩存中,讓運(yùn)算能快速進(jìn)行,當(dāng)運(yùn)算結(jié)束后再從緩存同步回內(nèi)存之中這樣處理器就無需等待緩慢的內(nèi)存讀寫了尺栖。
基于高速緩存的存儲交互很好地解決了處理器與內(nèi)存的速度差異矛盾,但同是引入了一個新的問題:緩存一致性烦租。在多處理器系統(tǒng)中延赌,每個處理器都有自己的高速緩存,而他們又共享同一主存叉橱,如下圖所示:多個處理器運(yùn)算任務(wù)都涉及同一塊主存挫以,需要一種協(xié)議可以保障數(shù)據(jù)的一致性,這類協(xié)議有MSI窃祝、MESI掐松、MOSI及Dragon Protocol等》嘈。總而言之大磺,緩存一致性解決了緩存中的數(shù)據(jù)同步到主內(nèi)存中數(shù)據(jù)一致性。
1.并發(fā)編程需要解決的關(guān)鍵問題
- 線程通信
線程間以什么機(jī)制來交換信息 - 線程同步
程序用于控制不同線程間操作發(fā)生相對順序的機(jī)制
2.java解決并發(fā)編程的方法
Java的并發(fā)采用的是共享內(nèi)存模型探膊。
-
線程通信
線程之間共享程序的公共狀態(tài)杠愧,線程之間通過寫-讀內(nèi)存中的公共狀態(tài)來進(jìn)行隱式通信。
-
線程同步
程序必須顯示地指定某個方法或某段代碼需要在線程之間互斥逞壁。
3.JMM內(nèi)存交互
Java內(nèi)存模型定義了以下八種操作來完成流济,如下圖所示:
lock(鎖定):作用于主內(nèi)存的變量,把一個變量標(biāo)識為一條線程獨(dú)占狀態(tài)猾担。
unlock(解鎖):作用于主內(nèi)存變量,把一個處于鎖定狀態(tài)的變量釋放出來刺下,釋放后的變量才可以被其他線程鎖定绑嘹。
read(讀取):作用于主內(nèi)存變量,把一個變量值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中橘茉,以便隨后的load動作使用工腋。
load(載入):作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中畅卓。
use(使用):作用于工作內(nèi)存的變量擅腰,把工作內(nèi)存中的一個變量值傳遞給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個需要使用變量的值的字節(jié)碼指令時將會執(zhí)行這個操作翁潘。
assign(賦值):作用于工作內(nèi)存的變量趁冈,它把一個從執(zhí)行引擎接收到的值賦值給工作內(nèi)存的變量,每當(dāng)虛擬機(jī)遇到一個給變量賦值的字節(jié)碼指令時執(zhí)行這個操作。
store(存儲):作用于工作內(nèi)存的變量渗勘,把工作內(nèi)存中的一個變量的值傳送到主內(nèi)存中沐绒,以便隨后的write的操作。
write(寫入):作用于主內(nèi)存的變量旺坠,它把store操作從工作內(nèi)存中一個變量的值傳送到主內(nèi)存的變量中乔遮。
JMM對8種內(nèi)存交互操作制定的規(guī)則:
不允許read、load取刃、store蹋肮、write操作之一單獨(dú)出現(xiàn),也就是read操作后必須load璧疗,store操作后必須write坯辩。
不允許線程丟棄他最近的assign操作,即工作內(nèi)存中的變量數(shù)據(jù)改變了之后病毡,必須告知主存濒翻。
不允許線程將沒有assign的數(shù)據(jù)從工作內(nèi)存同步到主內(nèi)存。
一個新的變量必須在主內(nèi)存中誕生啦膜,不允許工作內(nèi)存直接使用一個未被初始化的變量有送。就是對變量實施use、store操作之前僧家,必須經(jīng)過load和assign操作雀摘。
一個變量同一時間只能有一個線程對其進(jìn)行l(wèi)ock操作。多次lock之后八拱,必須執(zhí)行相同次數(shù)unlock才可以解鎖阵赠。
如果對一個變量進(jìn)行l(wèi)ock操作,會清空所有工作內(nèi)存中此變量的值肌稻。在執(zhí)行引擎使用這個變量前清蚀,必須重新load或assign操作初始化變量的值。
如果一個變量沒有被lock爹谭,就不能對其進(jìn)行unlock操作枷邪。也不能unlock一個被其他線程鎖住的變量。
一個線程對一個變量進(jìn)行unlock操作之前诺凡,必須先把此變量同步回主內(nèi)存东揣。
4. 線程/工作內(nèi)存/主內(nèi)存 三者的關(guān)系
每個線程都有一個獨(dú)立的工作內(nèi)存,用于存儲線程私有的數(shù)據(jù)
Java內(nèi)存模型中規(guī)定所有變量都存儲在主內(nèi)存腹泌,主內(nèi)存是共享內(nèi)存區(qū)域嘶卧,所有線程都可以訪問
線程對變量的操作(讀取賦值等)必須在工作內(nèi)存中進(jìn)行。(線程安全問題的根本原因)
主內(nèi)存是在運(yùn)行期間所有變量的存放區(qū)域凉袱,當(dāng)工作內(nèi)存是運(yùn)行期間中某一線程獨(dú)立私有的內(nèi)存存放區(qū)域
線程間無法訪問對方的工作內(nèi)存空間芥吟,都是通過主內(nèi)存交換來實現(xiàn)
主內(nèi)存的變量在工作內(nèi)存中的值是復(fù)制過去的副本,讀寫完成后刷新主內(nèi)存,這意味著主內(nèi)存如果發(fā)生了改變运沦,工作內(nèi)存并無法獲得最新的結(jié)果
多個線程對一個共享變量進(jìn)行修改時泵额,都是對自己工作內(nèi)存的副本進(jìn)行操作,相互不可見携添。主內(nèi)存最后得到的結(jié)果是不可預(yù)知的嫁盲。
參考文章:
[(翻譯)JVM——Java物理內(nèi)存模型(JMM)及運(yùn)行時內(nèi)存模型]https://blog.csdn.net/cheya3213/article/details/100595911