浅谈 MESI 缓存一致性协议

 2022-09-22

MESI场景

下面这个程序缓存一致性问题,initFlag第二个线程更改为true后,如果没有volatile关键字修饰,thread1是无法可见数据已被修改,不会跳出while循环。

202209222333243791.png

202209222333265182.png

可见性实现一

当对代码initFlag进行修饰关键字volatile时,可实现数据修改的可见性

202209222333274613.png

可见性实现二

202209222333286694.png

如上图Console打印结果,很奇怪为什么initFlag没有被volatile修饰也可以实现initFlag变量的可见性,那是为什么呢?为什么线程1能够将自己的工作内存副本清除去主内存获取到线程2更改后的值呢?(不用volatile关键字)

答案是MESI缓存一致性协议的原理所致,当MESI收到一个#LOCK汇编指令时,会强制所有的线程的副本数据进行数据同步,那#LOCK汇编指令在Java中,是如何体现出来,自然是synchronized。

我们只是增加了一行,就实现了volatile的功能

    					System.out.println("进来了");

我们看下源码就知道了

    /**
     * Prints a String and then terminate the line. This method behaves as
     * though it invokes {@link #print(String)} and then
     * {@link #println()}.
     *
     * @param x The String 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汇编指令时,就会进行线程中的副本数据同步,而上面的例子中,只是用了一个取巧的案例来说明其中的缘由。