我們都知道,volatile保證了記憶體可見性和禁止指令重排,但是對于記憶體可見性這一條,我一直沒有完全弄明白,今天咱們一起看一下,這個可見性,到底是如何可見,資料到底是如何可見的,
首先我們要達成一個共識:單核CPU由于同一時刻只會有一個執行緒執行,而每個執行緒執行的時候操作的都是同一個CPU的快取,所以,單核CPU不存在可見性問題,
要了解清楚什么是記憶體可見性,我們需要先明確幾個關鍵字的含義,
定義
cpu快取

今天主流的CPU架構來說,現在的CPU主要采用三層快取:
L1、L2快取成為本地核心內快取,即一個核一個,如果你的機器是4核,
那就是有4個L1+4個L2
L3快取是所有核共享的,即不管你的CPU是幾核,這個CPU中只有一個L3
L1快取的大小是64K,即32K指令快取+32K資料快取,L2是256K,L3是2M,
MESI協議
MESI其實對應的是快取中快取行(CPU快取存盤資料的最小單位,大小為64B)的四種狀態,
- 已修改Modified (M)
快取行是臟的(dirty),與主存的值不同,如果別的CPU內核要讀主存這塊資料,該快取行必須回寫到主存,狀態變為共享(S). - 獨占Exclusive (E)
快取行只在當前快取中,但是干凈的(clean)--快取資料同于主存資料,當別的快取讀取它時,狀態變為共享;當前寫資料時,變為已修改狀態,
3.共享Shared (S)
快取行也存在于其它快取中且是干凈的,快取行可以在任意時刻拋棄,
4.無效Invalid (I)
快取行是無效的
舉例
明確了定義,接下來我們用一個具體示例來描述一下MESI的可見性是如何起效的,
i=0; i=i+1;
執行這條陳述句的時候,在某個核上運行的某執行緒將 i 的值拷貝一個副本到此核所在的快取中,當運算執行完成后,再回寫到主存中去,如果是多執行緒環境下,每一個執行緒都會在所運行的核上的高速快取區有一個對應的作業記憶體,也就是每一個執行緒都有自己的私有作業快取區,用來存放運算需要的副本資料,那么,我們再來看這個 i+1 的問題,假設 i 的初始值為0,有兩個執行緒同時執行這條陳述句,有以下幾個步驟:
- 程式以及資料被加載到主記憶體
- 指令和資料被加載到CPU的高速快取
- CPU執行指令,把結果寫到高速快取
- 高速快取中的資料寫回主記憶體
我們先討論volatile是如何保證可見性的,假設此時執行緒1被cpu0執行,執行緒2被cpu1執行,cpu0和cpu1同時把i=0這個值從記憶體讀取到了自己的快取中,那么,根據MESI協議,此時cpu0和cpu1中的該快取狀態都變成S(共享),然后此時cpu0計算完成i+1=1,cpu1也計算完成i+1=1,cpu0首先將結果寫回了主存,此時,cpu1快取中的i值,將會變為I(無效)狀態,此后如果cpu1再去讀取i,就要重新從主存拿值,可是此時cpu1其實已經計算完成,所以cpu1也將結果1寫回了主存,
從這個例子看,雖然保證了可見性,可是最終的計算結果還是錯誤的,這是因為volatile沒有保證原子性,執行緒執行的這個i=i+1不是一個完整的不可分割的計算程序,
參考
Volatile:記憶體屏障原理應該沒有比這篇文章講的更清楚了
MESI協議
Java 開發, volatile 你必須了解一下
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/299560.html
標籤:Java
上一篇:面試官:生成訂單 30 分鐘未支付,則自動取消,該怎么實作?
下一篇:110_SSM框架
