一、概念层面的可见性
什么是线程间的可见性?
一个线程对共享变量值的修改,能够及时的被其他线程看到。
public class App {
public static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
int i = 0;
while(!stop){
i++;
}
});
t1.start();
Thread.sleep(1000);
stop = true;
}
}
上面代码运行会发现,stop 就算值已经被修改了。也没有办法,
线程也不会读取到,程序处于死循环之中,不会退出。
引出下面问题,线程可见性及其基本原理
如果添加了 volatile 情况就会不一样
public volatile static boolean stop = false;
线程保证可见性
1.volatile 保证了线程可见性
2.多了一个Lock 的汇编指令
可见性到底是什么?两个层面分析
- 硬件
- JMM层面
CPU 内存、 I/O 设备 读取速度差异
为了最大化利用CPU资源
- CPU增加高速缓存
- 引入线程、进程
- 指令优化 - > 重排序
二、CPU高速缓存,缓存导致不一致问题
cpu层面解决
- 总线锁 (总线上加锁,效率低)
- 缓存锁 (优化后)
基于MESI 协议可以解决缓存一致性的问题
MESI 表达缓存数据的四种状态
m : modify 、 e : exclusive 、 s :shared 、 i : invalid
![](https://img.haomeiwen.com/i11185115/a8e5951c835fc4a2.png)
MESI协议带来的问题 (CPU 通信)
![](https://img.haomeiwen.com/i11185115/16ab743e1162466d.png)
cpu 在读缓存过程中,发送消息给其他cpu 时候其他cpu 缓存失效并且在发
送指令给,读取cpu 中,这段时间读取缓存CPU需要阻塞,否则无法保证
缓存一致性,这样也会导致CUP利用率不够。
提出解决方案storebuffer 存储缓冲
比如你需要修改本地缓存中的一条信息,那么你必须将I(无效)状态通知到其他拥有该缓存数据的CPU缓存中,并且等待确认。等待确认的过程会阻塞处理器,这会降低处理器的性能。因为这个等待远远比一个指令的执行时间长的多。当所有失效确认(Invalidate Acknowledge)都接收到时,数据才会最终被提交。
这么做有也有风险,这里不作讨论
网友评论