介紹
之前介紹了java并發(fā)包的cas原理和java內(nèi)存模型,這篇我們介紹下cpu緩存一致性原理,可以幫助我們更好的理解cas的底層原理。
一、cpu多級緩存結(jié)構(gòu)
-
計算機在寄存器上執(zhí)行的速度是遠大于在主內(nèi)存上執(zhí)行的速度。
-
由于計算機的存儲設(shè)備與處理器的運算速度之間存在幾個數(shù)量級的差距,所以新的計算機系統(tǒng)都不得不加入一層讀寫速度都盡可能接近處理器運算速度的高級緩存來作為內(nèi)存與處理器之間的緩沖,將運算使用到的數(shù)據(jù)復(fù)制到緩存中,讓運算快速執(zhí)行,當(dāng)運算結(jié)束后,再將數(shù)據(jù)從緩存同步到內(nèi)存中,這樣處理器就無需等待緩慢的內(nèi)存讀寫了。
-
一個計算機還包含一個主存,所有的cpu都可以訪問這個主存,主存通常比CPU中的緩存大得多。
-
多核cpu運作原理:
通常情況下,當(dāng)一個CPU需要讀取主存的時候,它會將主存的數(shù)據(jù)讀取到CPU緩存中,甚至?xí)⒕彺嬷械牟糠謨?nèi)容讀到它內(nèi)部的寄存器里面,然后在寄存器中執(zhí)行操作;當(dāng)CPU需要將結(jié)果回寫到主存的時候,它會將內(nèi)部寄存器的值刷新到緩存中,然后在某個時間點將值刷新回主存。
二、MESI協(xié)議
四種狀態(tài):
-
M:Modified(被修改)
指的是該緩存行只被緩存在該CPU的緩存中,并且是被修改過的,因此它與主存中的數(shù)據(jù)是不一致的,該緩存行的內(nèi)存需要在未來的某個時間點寫回主存,這個時間點我們是允許其他CPU讀取主存中相應(yīng)的內(nèi)存之前,當(dāng)這里的值被寫回主存之后,該緩存行的狀態(tài)變成E。
-
E:Exclusive(獨享)
獨享狀態(tài)的緩存行只被緩存在該CPU的緩存中,它是未被修改過的,是與主存中的數(shù)據(jù)一致的,這個狀態(tài)可以在任何時刻,當(dāng)有其他CPU讀取該內(nèi)存時變成S。同樣的,當(dāng)COU修改該緩存行的數(shù)據(jù)時,該狀態(tài)可以變成M。
-
S:Shared(共享)
共享狀態(tài)意味著,該緩存行可能被多個CPU進行緩存,并且各個緩存中的數(shù)據(jù)與主存中的數(shù)據(jù)是一致的,當(dāng)有一個CPU修改該緩存行的時候,其他CPU中該緩存行是可以被作廢的,變成I。
-
I:Invalid(無效的)
無效狀態(tài)代表這個緩存是無效的,可能是有其他CPU修改了該緩存行。
CPU的cache的四種操作可能產(chǎn)生不一致的狀態(tài),因此緩存控制器監(jiān)聽到本地操作和遠程操作的時候,需要對地址一定的cache line做出一定的修改,從而保證數(shù)據(jù)在多個緩存之間的一致性。
四種操作:
-
local read
代表的是讀本地緩存行中的數(shù)據(jù)。
-
local write
代表的是將數(shù)據(jù)寫入到本地的緩存里面。
-
remote read
代表的是將內(nèi)存中的數(shù)據(jù)讀取到本地。
-
remote write
代表的是將數(shù)據(jù)寫回到主存中。
三、MESI工作原理
在一個典型的多核系統(tǒng)中,每一個核都會有自己的緩存來共享主存總線,每一個相應(yīng)的CPU都會發(fā)出讀寫請求,而緩存的目的是為了減少CPU讀寫共享主存的次數(shù)。
- 一個緩存除了在Invalid狀態(tài)之外,都可以滿足CPU的讀請求;
- 一個寫請求,只有在該緩存行是M狀態(tài)或者E狀態(tài),才能被執(zhí)行,如果當(dāng)前狀態(tài)是處于S狀態(tài),它必須先將這個緩存中的該緩存行變成無效的狀態(tài),這個操作一般采取廣播的方式來完成,這個時候,它不允許不同的CPU同時修改同一個緩存行,主要是為了解決緩存一致性的問題;
- 一個處于M狀態(tài)的緩存行,它必須時刻監(jiān)聽所有試圖讀該緩存行的操作,這種操作必須將緩存寫回到主內(nèi)存,并將狀態(tài)修改為S之前被延遲執(zhí)行;
- 一個處于S狀態(tài)的緩存行,也必須監(jiān)聽其他緩存只被緩存行無效,或者獨享該緩存行的請求,并將緩存行變成無效;
- 而一個處于E狀態(tài)的緩存行,它要監(jiān)聽其他緩存讀緩存中該緩存行的操作,一旦有該緩存行的操作,它需要將E狀態(tài)修改為S狀態(tài);
因此對于M和E,它的數(shù)據(jù)總是精確的,而S狀態(tài)可能是非一致的,如果一個緩存將處于S狀態(tài)的緩存行作廢了,另一個緩存實際上可能已經(jīng)獨享了該緩存行,但是該緩存卻不會將緩存行變更為E狀態(tài),這是因為其他緩存不會廣播他們作廢掉該緩存行的通知,同樣,由于緩存并沒有保存該緩存行的topic數(shù)量,因此也沒有辦法確認自己是否已經(jīng)獨享了該緩存行。
如果一個CPU想修改處于S狀態(tài)的緩存行,總線事務(wù)需要將所有該緩存行topic的值變成Invalid狀態(tài)才可以,而修改E狀態(tài)的緩存不需要使用總線事務(wù)。
四、MESI在并發(fā)包中的應(yīng)用
在cas原理分析中,我們講到cas底層原理使用到了總線鎖和緩存鎖,其中緩存鎖涉及到的就是MESI協(xié)議,緩存鎖加鎖條件就是緩存行處于M和E狀態(tài)下。
下面結(jié)合volatile保證內(nèi)存可見性為例,闡釋下MESI的工作原理。代碼示例:
public class VolatileCanSeeTest {
private static boolean initFlag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() - > {
log.info("init begin");
while(!initFlag) {
}
// if(!initFlag) {while(true){}} // JIT
log.info("===success===");
}).start();
Thread.sleep(1000);
new Thread(() - > doSomething()).start();
}
public static void doSomething() {
log.info("doSomething begin");
initFlag = true;
log.info("doSomething end");
}
}
- 初始化共享變量initFlag = false。
- 線程1通過read指令從主內(nèi)存中讀取出共享變量initFlag = false,通過load指令加載到線程1的工作內(nèi)存中。
- 在線程1的工作內(nèi)存中,通過use指令將共享變量initFlag = false加載到cpu執(zhí)行引擎進行!initFlag運算。
- 在線程1的工作內(nèi)存中,通過assign指令將cpu執(zhí)行引擎計算后的共享變量initFlag = true賦值到線程1的工作內(nèi)存中。
- 線程1通過store指令將線程1中工作內(nèi)存的共享變量同步到主內(nèi)存中。
- 在線程1通過store指令將線程1中工作內(nèi)存的共享變量同步到主內(nèi)存中的過程中,會經(jīng)過總線,觸發(fā)cpu緩存一致性協(xié)議。
- 該協(xié)議會監(jiān)聽回寫主內(nèi)存的變量,然后將其他工作內(nèi)存中含有該共享變量的緩存行狀態(tài)置為失效狀態(tài),所以其他線程需要重新從主內(nèi)存讀取該共享變量的最新值。
- 以上我們分析了MESI協(xié)議中關(guān)于S共享狀態(tài)轉(zhuǎn)為I失效狀態(tài)的工作原理。
結(jié)語
上一篇我們分析了java內(nèi)存模型的原理,這篇總結(jié)下cas涉及到的cpu緩存一致性協(xié)議,通過這兩篇的介紹,我們就可以更深刻的理解cas是怎么保證原子性的。
-
處理器
+關(guān)注
關(guān)注
68文章
19286瀏覽量
229853 -
控制器
+關(guān)注
關(guān)注
112文章
16361瀏覽量
178071 -
寄存器
+關(guān)注
關(guān)注
31文章
5343瀏覽量
120377 -
狀態(tài)機
+關(guān)注
關(guān)注
2文章
492瀏覽量
27541 -
JAVA語言
+關(guān)注
關(guān)注
0文章
138瀏覽量
20095
發(fā)布評論請先 登錄
相關(guān)推薦
評論