产生背景
前面在说volatile关键字的时候大体讲过,硬件层面由于CUP和内存运行速度差距很大,所以就引入了高速缓存,如下图:
而高速缓存的出现,就导致了数据可见性的问题,如下图
两个线程不能及时的知道对方更新的数据,为了达到数据访问的一致,需要各个处理器在访问缓存时遵循一些协议,在读写时根据协议来操作,这就引出了缓存一致性协议。
常见的协议有MSI,MESI,MOSI等。最常见的就是MESI协议。接下来给就以MESI协议来简单分析一下
什么是MESI
MESI表示缓存行的四种状态,分别是
1. M(Modify): 表示共享数据只缓存在当前CPU缓存中,并且被修改状态,也就是缓存的数据和主内存中的数据不一致
2. E(Exclusive): 表示缓存的独占状态,数据只缓存在当前CPU缓存中,并且没有被修改
**3. S(Shared) :**表示数据可能被多个CPU缓存,并且各个缓存中的数据和主内存数据一致
**4. I(Invalid) :**表示缓存已经失效
图解各个状态:
cache表示每个CPU里面的工作内存
a.CUP将内存中变量读取到工作内存,此时变量缓存行为S状态(共享状态)
b.CPU1更改了变量的值,此时CPU1变量缓存行表示为M状态(修改状态)
c.CPU1修改了变量值以后,会通知CPU2,将CPU2工作内存中的变量缓存行为I状态(失效状态)
d.假设CPU2中线程没有用到变量,只在CPU1线程中用到了变量,那么变量只在CPU1工作内存中存在,那么此时CPU1中变量缓存行为E状态(独占状态)
各个状态下的相互转化要理清
MESI带来的问题
举个例子,比如我们修改CPU0本地缓存中的一条信息,那么我们需要把当前状态更改为M状态,而且通知其他拥有该缓存数据的CPU1将缓存中数据置为I状态,并且等待确认返回。等待确认的过程会阻塞处理器,这会降低处理器的性能,我们将这个过程抽象为下图:
!](https://img-blog.csdnimg.cn/202005281833057.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM1NTc2OTc2,size_16,color_FFFFFF,t_70)
虽然这个阻塞时间很短,应为这个等待远远比一个指令的执行时间长的多,所以针对MESI进行了优化,引入了Store Bufferes
Store Bufferes
Store Bufferes是一个写的缓冲,对于上述描述的情况,CPU0可以先把写入的操作先存储到StoreBufferes中,Store Bufferes中的指令再按照缓存一致性协议去发起其他CPU缓存行的失效。而同步来说CPU0可以不用等到Acknowledgement,继续往下执行其他指令,直到收到CPU0收到Acknowledgement再更新到缓存,再从缓存同步到主内存,图示如下:
指令重排序
从上图我们可以看到,在store buffer收到其他CPU的ACK通知把变量更新到工作内存的时候,前后都会有指令在执行,倘若在之前用到了此变量的值,那么运行的结果就会和预期不一样,其实这就是指令重排序的根本原因,也就会产生所说的有序性问题
那么现在就能分析下面的代码产生不同结果的原因了吧
public class VolatileDemo02 {
public static int x = 0, y = 0;
public static void main(String[] args) throws InterruptedException {
Map<String,Integer> map = new HashMap<>();
for(int i=0;i<100000;i++){
x=0;
y=0;
Thread one = new Thread(()->{
int a = y;
x = 1;
map.put("a",a);
});
Thread two = new Thread(()->{
int b = x;
y = 1;
map.put("b",b);
});
one.start();
two.start();
one.join();
two.join();
System.out.println("a=" + map.get("a")+", "+"b=" + map.get("b"));
}
}
}
打印结果按不同结果总结(可自行尝试),如下:
a=0, b=1
a=1, b=0
a=1, b=1
a=0, b=0
Process finished with exit code 0
怎么解决有序性问题呢
通过内存屏障防止指令重排序,解决有序性问题
相关链接
Java内存模型(JMM)
内存屏障
本文是综合自己的认识和参考各类资料(书本及网上资料)编写,若有侵权请联系作者,所有内容仅代表个人认知观点,如有错误,欢迎校正; 邮箱:1354518382@qq.com 博客地址:https://blog.csdn.net/qq_35576976/