介紹
之前介紹了java并發包的cas原理和java內存模型,這篇我們介紹下cpu緩存一致性原理,可以幫助我們更好的理解cas的底層原理。
一、cpu多級緩存結構
-
計算機在寄存器上執行的速度是遠大于在主內存上執行的速度。
-
由于計算機的存儲設備與處理器的運算速度之間存在幾個數量級的差距,所以新的計算機系統都不得不加入一層讀寫速度都盡可能接近處理器運算速度的高級緩存來作為內存與處理器之間的緩沖,將運算使用到的數據復制到緩存中,讓運算快速執行,當運算結束后,再將數據從緩存同步到內存中,這樣處理器就無需等待緩慢的內存讀寫了。
-
一個計算機還包含一個主存,所有的cpu都可以訪問這個主存,主存通常比CPU中的緩存大得多。
-
多核cpu運作原理:
通常情況下,當一個CPU需要讀取主存的時候,它會將主存的數據讀取到CPU緩存中,甚至會將緩存中的部分內容讀到它內部的寄存器里面,然后在寄存器中執行操作;當CPU需要將結果回寫到主存的時候,它會將內部寄存器的值刷新到緩存中,然后在某個時間點將值刷新回主存。
二、MESI協議
四種狀態:
-
M:Modified(被修改)
指的是該緩存行只被緩存在該CPU的緩存中,并且是被修改過的,因此它與主存中的數據是不一致的,該緩存行的內存需要在未來的某個時間點寫回主存,這個時間點我們是允許其他CPU讀取主存中相應的內存之前,當這里的值被寫回主存之后,該緩存行的狀態變成E。
-
E:Exclusive(獨享)
獨享狀態的緩存行只被緩存在該CPU的緩存中,它是未被修改過的,是與主存中的數據一致的,這個狀態可以在任何時刻,當有其他CPU讀取該內存時變成S。同樣的,當COU修改該緩存行的數據時,該狀態可以變成M。
-
S:Shared(共享)
共享狀態意味著,該緩存行可能被多個CPU進行緩存,并且各個緩存中的數據與主存中的數據是一致的,當有一個CPU修改該緩存行的時候,其他CPU中該緩存行是可以被作廢的,變成I。
-
I:Invalid(無效的)
無效狀態代表這個緩存是無效的,可能是有其他CPU修改了該緩存行。
CPU的cache的四種操作可能產生不一致的狀態,因此緩存控制器監聽到本地操作和遠程操作的時候,需要對地址一定的cache line做出一定的修改,從而保證數據在多個緩存之間的一致性。
四種操作:
-
local read
代表的是讀本地緩存行中的數據。
-
local write
代表的是將數據寫入到本地的緩存里面。
-
remote read
代表的是將內存中的數據讀取到本地。
-
remote write
代表的是將數據寫回到主存中。
三、MESI工作原理
在一個典型的多核系統中,每一個核都會有自己的緩存來共享主存總線,每一個相應的CPU都會發出讀寫請求,而緩存的目的是為了減少CPU讀寫共享主存的次數。
- 一個緩存除了在Invalid狀態之外,都可以滿足CPU的讀請求;
- 一個寫請求,只有在該緩存行是M狀態或者E狀態,才能被執行,如果當前狀態是處于S狀態,它必須先將這個緩存中的該緩存行變成無效的狀態,這個操作一般采取廣播的方式來完成,這個時候,它不允許不同的CPU同時修改同一個緩存行,主要是為了解決緩存一致性的問題;
- 一個處于M狀態的緩存行,它必須時刻監聽所有試圖讀該緩存行的操作,這種操作必須將緩存寫回到主內存,并將狀態修改為S之前被延遲執行;
- 一個處于S狀態的緩存行,也必須監聽其他緩存只被緩存行無效,或者獨享該緩存行的請求,并將緩存行變成無效;
- 而一個處于E狀態的緩存行,它要監聽其他緩存讀緩存中該緩存行的操作,一旦有該緩存行的操作,它需要將E狀態修改為S狀態;
因此對于M和E,它的數據總是精確的,而S狀態可能是非一致的,如果一個緩存將處于S狀態的緩存行作廢了,另一個緩存實際上可能已經獨享了該緩存行,但是該緩存卻不會將緩存行變更為E狀態,這是因為其他緩存不會廣播他們作廢掉該緩存行的通知,同樣,由于緩存并沒有保存該緩存行的topic數量,因此也沒有辦法確認自己是否已經獨享了該緩存行。
如果一個CPU想修改處于S狀態的緩存行,總線事務需要將所有該緩存行topic的值變成Invalid狀態才可以,而修改E狀態的緩存不需要使用總線事務。
四、MESI在并發包中的應用
在cas原理分析中,我們講到cas底層原理使用到了總線鎖和緩存鎖,其中緩存鎖涉及到的就是MESI協議,緩存鎖加鎖條件就是緩存行處于M和E狀態下。
下面結合volatile保證內存可見性為例,闡釋下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指令從主內存中讀取出共享變量initFlag = false,通過load指令加載到線程1的工作內存中。
- 在線程1的工作內存中,通過use指令將共享變量initFlag = false加載到cpu執行引擎進行!initFlag運算。
- 在線程1的工作內存中,通過assign指令將cpu執行引擎計算后的共享變量initFlag = true賦值到線程1的工作內存中。
- 線程1通過store指令將線程1中工作內存的共享變量同步到主內存中。
- 在線程1通過store指令將線程1中工作內存的共享變量同步到主內存中的過程中,會經過總線,觸發cpu緩存一致性協議。
- 該協議會監聽回寫主內存的變量,然后將其他工作內存中含有該共享變量的緩存行狀態置為失效狀態,所以其他線程需要重新從主內存讀取該共享變量的最新值。
- 以上我們分析了MESI協議中關于S共享狀態轉為I失效狀態的工作原理。
結語
上一篇我們分析了java內存模型的原理,這篇總結下cas涉及到的cpu緩存一致性協議,通過這兩篇的介紹,我們就可以更深刻的理解cas是怎么保證原子性的。
-
處理器
+關注
關注
68文章
19178瀏覽量
229200 -
控制器
+關注
關注
112文章
16214瀏覽量
177479 -
寄存器
+關注
關注
31文章
5325瀏覽量
120052 -
狀態機
+關注
關注
2文章
492瀏覽量
27486 -
JAVA語言
+關注
關注
0文章
138瀏覽量
20079
發布評論請先 登錄
相關推薦
評論