java內(nèi)存模型(Java Memory Model,JMM)是java虛擬機(jī)規(guī)范定義的,用來(lái)屏蔽掉java程序在各種不同的硬件和操作系統(tǒng)對(duì)內(nèi)存的訪問(wèn)的差異,這樣就可以實(shí)現(xiàn)java程序在各種不同的平臺(tái)上都能達(dá)到內(nèi)存訪問(wèn)的一致性。可以避免像c++等直接使用物理硬件和操作系統(tǒng)的內(nèi)存模型在不同操作系統(tǒng)和硬件平臺(tái)下表現(xiàn)不同,比如有些c/c++程序可能在windows平臺(tái)運(yùn)行正常,而在linux平臺(tái)卻運(yùn)行有問(wèn)題。
MESI緩存協(xié)議
CPU高速緩存(Cache Memory)
CPU為何要有高速緩存?
CPU在摩爾定律的指導(dǎo)下以每18個(gè)月翻一番的速度在發(fā)展,然而內(nèi)存和硬盤的發(fā)展速度遠(yuǎn)遠(yuǎn)不及CPU。這就造成了高性能能的內(nèi)存和硬盤價(jià)格及其昂貴。然而CPU的高度運(yùn)算需要高速的數(shù)據(jù)。為了解決這個(gè)問(wèn)題,CPU廠商在CPU中內(nèi)置了少量的高速緩存以解決I\O速度和CPU運(yùn)算速度之間的不匹配問(wèn)題。
在CPU訪問(wèn)存儲(chǔ)設(shè)備時(shí),無(wú)論是存取數(shù)據(jù)抑或存取指令,都趨于聚集在一片連續(xù)的區(qū)域中,這就被稱為局部性原理。
* 時(shí)間局部性(Temporal Locality):如果一個(gè)信息項(xiàng)正在被訪問(wèn),那么在近期它很可能還會(huì)被再次訪問(wèn)。比如循環(huán)、遞歸、方法的反復(fù)調(diào)用等。
* 空間局部性(Spatial Locality):如果一個(gè)存儲(chǔ)器的位置被引用,那么將來(lái)他附近的位置也會(huì)被引用。比如順序執(zhí)行的代碼、連續(xù)創(chuàng)建的兩個(gè)對(duì)象、數(shù)組等。
帶有高速緩存的CPU執(zhí)行計(jì)算的流程
1.程序以及數(shù)據(jù)被加載到主內(nèi)存
2.指令和數(shù)據(jù)被加載到CPU的高速緩存
3.CPU執(zhí)行指令,把結(jié)果寫到高速緩存
4.高速緩存中的數(shù)據(jù)寫回主內(nèi)存
多核CPU多級(jí)緩存一致性協(xié)議MESI
多核CPU的情況下有多個(gè)一級(jí)緩存,如何保證緩存內(nèi)部數(shù)據(jù)的一致,不讓系統(tǒng)數(shù)據(jù)混亂。這里就引出了一個(gè)一致性的協(xié)議MESI。如下圖:

MESI協(xié)議緩存狀態(tài)
緩存行(Cache line):緩存存儲(chǔ)數(shù)據(jù)的單元
1.M:修改,該Cache line有效,數(shù)據(jù)被修改了,和內(nèi)存中的數(shù)據(jù)不一致,數(shù)據(jù)只存在于本Cache中
2.E:獨(dú)占,該Cache line有效,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致,數(shù)據(jù)只存在于本Cache中。
3.S:共享,該Cache line有效,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致,數(shù)據(jù)存在于很多Cache中。
4.I:無(wú)效,該Cache line無(wú)效。
JMM-8大原子操作
物理機(jī)高速緩存和主內(nèi)存之間的交互有協(xié)議,同樣的,java內(nèi)存中線程的工作內(nèi)存和主內(nèi)存的交互是由java虛擬機(jī)定義了如下的8種操作來(lái)完成的,每種操作必須是原子性的。java虛擬機(jī)中主內(nèi)存和工作內(nèi)存交互,就是一個(gè)變量如何從主內(nèi)存?zhèn)鬏數(shù)焦ぷ鲀?nèi)存中,如何把修改后的變量從工作內(nèi)存同步回主內(nèi)存。
1.lock (鎖定):作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)記為一條線程獨(dú)占狀態(tài)
2.unlock (解鎖):作用于主內(nèi)存的變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后變量才可以被其他線程鎖定
3.read (讀取):把一個(gè)變量值從主存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load動(dòng)作使用
4.load (載入):它把read操作從主內(nèi)存中得到的變量值放入工作的變量副本中
5.use (使用):把工作內(nèi)存中的一個(gè)變量值傳遞給執(zhí)行引擎
6.assign(賦值):它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存中的變量
7.store(存儲(chǔ)):把工作內(nèi)存中的一個(gè)變量的值傳送到主存中
8.write(寫入):把store操作從工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存的變量中
假設(shè)執(zhí)行以下代碼:
var flag = false
//線程1
Thread {
if (flag) {......}
}
//線程2
Thread {
flag = true
}
以線程2為例,具體步驟:
1、執(zhí)行read操作,將數(shù)據(jù)從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存
2、執(zhí)行l(wèi)oad操作,主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中
3、執(zhí)行use操作,工作內(nèi)存中的變量值傳遞給執(zhí)行引擎
4、執(zhí)行assign操作,把從執(zhí)行引擎接收到的值賦給工作內(nèi)存中的變量
5、執(zhí)行store操作,把工作內(nèi)存中的變量的值傳送到主存中
6、執(zhí)行write操作,把store操作從工作內(nèi)存中傳送的變量值更新主內(nèi)存的變量中
如下圖:
