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 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汇编指令时,就会进行线程中的副本数据同步,而上面的例子中,只是用了一个取巧的案例来说明其中的缘由。