MESI场景
下面这个程序缓存一致性问题,initFlag第二个线程更改为true后,如果没有volatile关键字修饰,thread1是无法可见数据已被修改,不会跳出while循环。
可见性实现一
当对代码initFlag进行修饰关键字volatile时,可实现数据修改的可见性
可见性实现二
如上图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 TheString
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汇编指令时,就会进行线程中的副本数据同步,而上面的例子中,只是用了一个取巧的案例来说明其中的缘由。