MESI緩存一致性協議小談

MESI場景

下面這個程序緩存一致性問題,initFlag第二個線程更改為true後,如果沒有volatile關鍵字修飾,thread1是無法可見數據已被修改,不會跳出while循環。

MESI緩存一致性協議小談

MESI緩存一致性協議小談

可見性實現一

當對代碼initFlag進行修飾關鍵字volatile時,可實現數據修改的可見性

MESI緩存一致性協議小談

可見性實現二

MESI緩存一致性協議小談

如上圖Console打印結果,很奇怪為什麼initFlag沒有被volatile修飾也可以實現initFlag變量的可見性,那是為什麼呢?為什麼線程1能夠將自己的工作內存副本清除去主內存獲取到線程2更改後的值呢?(不用volatile關鍵字)

答案是MESI緩存一致性協議的原理所致,當MESI收到一個#LOCK彙編指令時,會強制所有的線程的副本數據進行數據同步,那#LOCK彙編指令在Java中,是如何體現出來,自然是synchronized。

我們只是增加了一行,就實現了volatile的功能

\t\t\t\t\tSystem.out.println("進來了");

我們看下源碼就知道了

/** * Prints a String and then terminate the line. This method behaves as * though it invokes <code>{@link #print(String)}/<code> and then * <code>{@link #println()}/<code>. * * @param x The <code>String/<code> to be printed.
*/
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}

源碼裡有synchronized同步代碼塊,在線程中存在同步塊時,會強制去主內存重新獲取主內存的值 可以試下把線程1的while循環裡的System.out.println()改為普通的賦值語句int a = 0;則線程1會卡住死循環。

小結

MESI的數據同步條件為收到一個#LOCK彙編指令時,就會進行線程中的副本數據同步,而上面的例子中,只是用了一個取巧的案例來說明其中的緣由。


分享到:


相關文章: