1:在目前主流的计算机中,CPU执行计算的主要流程如图所示
2:数据加载流程如下
- 1 将需要的数据从磁盘家在到内存中
- 2 将内存中的数据加载到缓存中(图中的L1,L2,L3)
- 3 CPU从缓存中拿到数据进行计算
- 4 将计算结果刷回缓存,并在一定的时间刷回内存
3:MESI出现背景
现在的CPU的一般都是多核,每个CPU都有自己独立的缓存,就是图中的L1,L2,L3,
如果这时候CPU1更新了数据,比如将a的值从1改成2,但是还没有刷回内存,
那么这时候CPU2从内存读取的a的值还是1,就出现了数据不一致的情况了
复制代码
4:对总线加锁
在Java程序中,为了线程安全我们一般都会选择加锁,比如Synchronized,Lock等,
但是对总线加锁会导致对其它数据的读写造成阻塞,整体性能下降
复制代码
5:MESI的原理
MESI的原理就类似HashTable和ConcurrentMap的特性一样,一个是直接在整个方法加锁,
一个是分段锁,MESI是针对某个缓存行进行加锁,它只会针对某个缓存行加锁,
不会影响其它数据的读写
复制代码
6:MESI协议解析
状态 | 描述 | 监听任务 |
---|---|---|
M 修改(Modify) | 该缓存行有效,数据被修改了,和内存中的数据不一致,数据只存在于缓存行中 | 缓存行必须时刻监听所有试图读该缓存行相对应的内存的操作,其他缓存须在本缓存行写回内存并将状态置为E之后才能操作该缓存行对应的内存数据 |
E 独享,互斥(Exclusive) | 该缓存行有效,数据和内存中的数据一致,数据只存在于本缓存行中 | 缓存行必须监听其他缓存读主内存中该缓存行相对应的内存的操作,一旦有这种操作,该缓存行需要变成S状态 |
S 共享 (Shared) | 该缓存行有效,数据和内存中的数据一致,数据同时存在于其他缓存中 | 缓存行必须监听其他缓存是该缓存行无效或者独享该缓存行的请求,并将该缓存行置为I状态 |
I 无效 (Invalid) | 该缓存行数据无效 | 无 |
7:MESI流程解析
- 1: CPU1将变量a从内存加载到自己的独立的缓存中,并将该变量a的状态设置为E(独享)状态,并通过总线嗅探技术对内存中a的操作进行嗅探
- 2:此时CPU2要读取变量a,总线嗅探到这个操作后就会将CPU1中变量a的状态从E(独享) 修改成 S(共享) 状态
- 3:如果这时候CPU1要修改a的值了,那么此时CPU1中的a的状态就会从S(共享)状态修改成M(修改)状态,而CPU2中的变量a的状态会从S(共享)状态修改成I(无效),这时候如果说CPU1和CPU2同时要修改a的值怎么办??这时候MESI会从CPU1和CPU2中选择其中一个,并将a的状态修改成 M(修改),其它的都变成 I(无效)
- 4:CPU1将a修改后的值写回内存,并将a的状态修改成E(独享)状态,后续其它CPU通过总线嗅探技术得知变量a的值已经被修改,就会从内存读取最新的值,并且CPU1和CPU2中a的状态就都会变成S(共享状态)
- 5:MESI协议只能保证数据的可见性,并不能保证有序性和原子性,所以不能只靠MESI来解决多线程中的安全问题
8:MESI无效场景
- 1 CPU不支持缓存一致性协议
- 2 MESI是对单独一个缓存行进行加锁,此时如果这个数据的大小超出了一个缓存行的大小,那么也会无效
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END