4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?
MESI一致性協(xié)議
小陳:老王,上一章你讓我看看MESI一致性協(xié)議,我大概了解了一下。
老王:哦,來說說你對MESI一致性協(xié)議的理解
小陳:MESI協(xié)議也叫做緩存一致性協(xié)議,主要是用來進(jìn)行協(xié)調(diào)多核CPU的高級緩存的數(shù)據(jù)一致的。第一章的時候講過,CPU多級緩存架構(gòu),存在多個高速緩存之間數(shù)據(jù)一致性的問題。
老王:哦,第一章講的多核CPU高速緩存之間的數(shù)據(jù)一致性問題,你還記得啊,哈哈,那你來說說。
小陳:當(dāng)然,當(dāng)時我可以記在小本本里面的,就等著你來講這事的呢,沿用第一篇的那個圖來講一下:
(1)看下面的圖,假如兩個CPU0、CPU1同時將 i = 0 讀取進(jìn)入自己的高速緩存
(2)然后CPU0執(zhí)行得比較快,執(zhí)行完 i++操作之后將 i=1 的結(jié)果刷回主存了
(3)但是CPU1并不知道數(shù)據(jù)已經(jīng)變更了,還是使用 i = 0 的結(jié)果去進(jìn)行 i++ 操作
(4)本來正確的結(jié)果應(yīng)該是 i = 2的,結(jié)果執(zhí)行了兩次i++,結(jié)果卻是1
老王:沒錯,當(dāng)時討論多核CPU的高速緩存的時候,留了這個問題;看來你還記得這個問題,很好很好,掌聲鼓勵一下!
小陳:嘿嘿,突然的夸我,怪不好意思的......
小陳:造成上面數(shù)據(jù)不一致的原因主要是因為,CPU0修改了數(shù)據(jù)之后,沒有機(jī)制能夠通知到CPU1,讓CPU1它高速緩存上這個變量的數(shù)據(jù)失效掉,導(dǎo)致CPU1計算的時候還是使用舊的值。
所以要解決多核CPU的高速緩存數(shù)據(jù)一致性的問題,必須有個機(jī)制能夠某個CPU0修改了數(shù)據(jù)之后,立即通知的CPU1,讓CPU1的高速緩存上的這個變量數(shù)據(jù)失效掉;然后CPU1用到的時候重新重主內(nèi)存讀取最新的值,這樣就能得到最新的結(jié)果了。
小陳:MESI實(shí)現(xiàn)的緩存一致性協(xié)議,正是CPU0修改了數(shù)據(jù),通知到CPU1的那套通知機(jī)制的一種規(guī)范,計算機(jī)廠商根據(jù)這套規(guī)范實(shí)現(xiàn)了這種通知機(jī)制,但是不同的廠商之間實(shí)現(xiàn)方式可能稍微不同。
老王:哎呀呀,不錯啊。看來兩天不見,你竟然把問題研究得這么深入了。
小陳:那是,問了看懂這個MESI協(xié)議,我可是連續(xù)肝了兩天晚上,點(diǎn)了無數(shù)根華子...
老王:孺子可教也,那你繼續(xù)來說說MESI的緩存一致性的內(nèi)容
小陳:MESI一致性協(xié)議定義了高速緩存中數(shù)據(jù)的4中狀態(tài),分別是:
M(Modified): 修改過的,只有一個CPU能獨(dú)占這個修改狀態(tài),獨(dú)占的意思是當(dāng)有一個CPU的高速緩存數(shù)據(jù)處于這個狀態(tài)的時候,其它CPU的高速緩存對這個共享的數(shù)據(jù)均不能操作;高速緩存中的數(shù)據(jù)發(fā)生了更新,需要被刷入主內(nèi)存中。
E(Exclusive): 獨(dú)占狀態(tài),只有一個CPU能獨(dú)占這個狀態(tài),同樣當(dāng)某個CPU的高速緩存的數(shù)據(jù)處于這個狀態(tài)的時候,其它CPU的均不能操作這個共享數(shù)據(jù)
S(Share):共享的狀態(tài),當(dāng)CPU的高速緩存中的數(shù)據(jù)這個狀態(tài)的時候,各個CPU可以并發(fā)的對這個數(shù)據(jù)進(jìn)行讀取
I(Invalid):無效的,意思是當(dāng)前高速緩存的這個數(shù)據(jù)已經(jīng)是無效了或者過期了,不能使用。
老王:那你繼續(xù)來說說,MESI這四種狀態(tài)是怎么來解決多核CPU高速緩存數(shù)據(jù)一直的?
小陳:好,下面我畫幾張圖來i講一下:
(1)首先像下面的圖一樣,CPU0、CPU1將共享變量 i = 0 讀取,進(jìn)入自己高速緩存的時候;緩存的狀態(tài)是S,也就是共享的
(2)然后CPU0要對 i = 0 的變量進(jìn)行修改操作,在MESI一致性里面大概會經(jīng)過這些步驟:
1. CPU0發(fā)送消息給總線,說我要修改數(shù)據(jù)了,幫我通知一下其它的CPU
2. 其它的CPU收到總線通知消息,將自己高速緩存上 i = 0 的數(shù)據(jù)狀態(tài)變?yōu)镮nvalid過期
3. 其它CPU返回給總線說我們都過期了
4. 總線收到其它CPU返回過期OK了
5. 總線返回給CPU0說,好了,其它CPU都通知到位了,它們高速緩存上的 i = 0的數(shù)據(jù)都是過期狀態(tài)了
6. CPU0收到了過期確認(rèn),都過期了,那我就可以獨(dú)占這份數(shù)據(jù)了,嘿嘿,準(zhǔn)備可以修改數(shù)據(jù)了
(3)CPU0修改數(shù)據(jù),刷新回主內(nèi)存,還會經(jīng)過這些步驟:
7. CPU0執(zhí)行 i++ 操作,將 i = 1 的最新結(jié)果刷入到高速緩存中,同時將高速緩存的數(shù)據(jù)狀態(tài)設(shè)為M(修改過的)
8. 然后將高速緩存中 i = 1 的最新結(jié)果又刷入主內(nèi)存中
(4)CPU1要讀取數(shù)據(jù)操作,發(fā)現(xiàn)高速緩存上數(shù)據(jù)過期了,回經(jīng)過下面步驟:
9. CPU1發(fā)現(xiàn)自己高速緩存上 i = 0 的數(shù)據(jù)是 Invalid 過期狀態(tài),于是從主存重新讀取
10. 然后CPU1從主內(nèi)存讀取到 i = 1的最新的數(shù)據(jù),將自己狀態(tài)設(shè)置成S
小陳:上面就是我理解的MESI一致性協(xié)議在多核CPU之間進(jìn)行協(xié)調(diào)的原理
老王:牛啊,小陳;短短兩天時間你理解到這個程度,這理解能力很不錯了,頗有我當(dāng)年的風(fēng)范啊.....
小陳:......
老王:就MESI一致性協(xié)議來說,你理解到這個程度已經(jīng)可以了;再往下層次就是各個計算機(jī)廠商在硬件層面實(shí)現(xiàn)的差異了,這個我也不懂了,理解到這里已經(jīng)非常不錯了...
小陳:老王,我記得你跟我說JAVA內(nèi)存模型的時候,每個線程在JMM中都有自己的工作內(nèi)存。每個線程的工作內(nèi)存都有共享變量的一個副本,這多個工作內(nèi)存的變量副本之間也可能存在數(shù)據(jù)不一致的情況。
老王:嗯嗯怎了了?
小陳:我回顧了之前JMM的圖,是這樣子的:
小陳:多個工作內(nèi)存之間共享變量x的副本,可能數(shù)據(jù)不一致,這個數(shù)據(jù)不一致的可見性問題在JMM層次是怎么解決的?
老王:你想想,Java內(nèi)存模型建立在多核CPU高速緩存之上對不對?
小陳:是的
老王:那JAVA內(nèi)存模型對應(yīng)到底層肯定也是操作主內(nèi)存、操作高速緩存來操作數(shù)據(jù)的,既然多核CPU的緩存一致性又是通過MESI協(xié)議來達(dá)到一致性的。所以啊,JAVA內(nèi)存模型底層其實(shí)也還是通過MESI一致性來使得一個線程修改了數(shù)據(jù),把別的線程的工作內(nèi)存副本數(shù)據(jù)弄失效的。
小陳:哦哦,原來JAVA內(nèi)存模型底層也是通過MESI來使得別的線程的工作內(nèi)存變量副本失效的啊。
老王:嗯嗯,沒錯的。
小陳:底層的原理我知道了,那在JAVA編程的層次又是怎么來做的?
老王:那就是下面要講的文章了,前面的幾篇文章和這一篇我們都是討論在底層原理層次。真正在使用層次來說,volatile、synchronized、cas、鎖等都用到了這些底層原理。
老王:下面我們就先來討論volatile這個關(guān)鍵字,看看它是怎么來保證可見性的?
小陳:好啊,我期待一波......
關(guān)注小陳,公眾號上更多更全的文章
JAVA并發(fā)文章目錄(公眾號)
JAVA并發(fā)專題 《筑基篇》
1.什么是CPU多級緩存模型?
2.什么是JAVA內(nèi)存模型?
3.線程安全之可見性、有序性、原子性是什么?
4.什么是MESI緩存一致性協(xié)議?怎么解決并發(fā)的可見性問題?
JAVA并發(fā)專題《練氣篇》
5.volatile怎么保證可見性?
6.什么是內(nèi)存屏障?具有什么作用?
7.volatile怎么通過內(nèi)存屏障保證可見性和有序性?
8.volatile為啥不能保證原子性?
9.synchronized是個啥東西?應(yīng)該怎么使用?
10.synchronized底層之monitor、對象頭、Mark Word?
11.synchronized底層是怎么通過monitor進(jìn)行加鎖的?
12.synchronized的鎖重入、鎖消除、鎖升級原理?無鎖、偏向鎖、輕量級鎖、自旋、重量級鎖
13.synchronized怎么保證可見性、有序性、原子性?
JAVA并發(fā)專題《結(jié)丹篇》
14. JDK底層Unsafe類是個啥東西?
15.unsafe類的CAS是怎么保證原子性的?
16.Atomic原子類體系講解
17.AtomicInteger、AtomicBoolean的底層原理
18.AtomicReference、AtomicStampReference底層原理
19.Atomic中的LongAdder底層原理之分段鎖機(jī)制
20.Atmoic系列Strimped64分段鎖底層實(shí)現(xiàn)源碼剖析
JAVA并發(fā)專題《金丹篇》
21.AQS是個啥?為啥說它是JAVA并發(fā)工具基礎(chǔ)框架?
22.基于AQS的互斥鎖底層源碼深度剖析
23.基于AQS的共享鎖底層源碼深度剖析
24.ReentrantLock是怎么基于AQS實(shí)現(xiàn)獨(dú)占鎖的?
25.ReentrantLock的Condition機(jī)制底層源碼剖析
26.CountDownLatch 門栓底層源碼和實(shí)現(xiàn)機(jī)制深度剖析
27.CyclicBarrier 柵欄底層源碼和實(shí)現(xiàn)機(jī)制深度剖析
28.Semaphore 信號量底層源碼和實(shí)現(xiàn)機(jī)深度剖析
29.ReentrantReadWriteLock 讀寫鎖怎么表示?
30. ReentrantReadWriteLock 讀寫鎖底層源碼和機(jī)制深度剖析
JAVA并發(fā)專題《元神篇》并發(fā)數(shù)據(jù)結(jié)構(gòu)篇
31.CopyOnAarrayList 底層分析,怎么通過寫時復(fù)制副本,提升并發(fā)性能?
32.ConcurrentLinkedQueue 底層分析,CAS 無鎖化操作提升并發(fā)性能?
33.ConcurrentHashMap詳解,底層怎么通過分段鎖提升并發(fā)性能?
34.LinkedBlockedQueue 阻塞隊列怎么通過ReentrantLock和Condition實(shí)現(xiàn)?
35.ArrayBlockedQueued 阻塞隊列實(shí)現(xiàn)思路竟然和LinkedBlockedQueue一樣?
36.DelayQueue 底層源碼剖析,延時隊列怎么實(shí)現(xiàn)?
37.SynchronousQueue底層原理解析
JAVA并發(fā)專題《飛升篇》線程池底層深度剖析
38. 什么是線程池?看看JDK提供了哪些默認(rèn)的線程池?底層竟然都是基于ThreadPoolExecutor的?
39.ThreadPoolExecutor 構(gòu)造函數(shù)有哪些參數(shù)?這些參數(shù)分別表示什么意思?
40.內(nèi)部有哪些變量,怎么表示線程池狀態(tài)和線程數(shù),看看道格.李大神是怎么設(shè)計的?
41. ThreadPoolExecutor execute執(zhí)行流程?怎么進(jìn)行任務(wù)提交的?addWorker方法干了啥?什么是workder?
42. ThreadPoolExecutor execute執(zhí)行流程?何時將任務(wù)提交到阻塞隊列? 阻塞隊列滿會發(fā)生什么?
43. ThreadPoolExecutor 中的Worker是如何執(zhí)行提交到線程池的任務(wù)的?多余Worker怎么在超出空閑時間后被干掉的?
44. ThreadPoolExecutor shutdown、shutdownNow內(nèi)部核心流程
45. 再回頭看看為啥不推薦Executors提供幾種線程池?
46. ThreadPoolExecutor線程池篇總結(jié)
總結(jié)
以上是生活随笔為你收集整理的4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 万能pdf转换器 V4.1
- 下一篇: 可视化 | pyecharts之柱状图常