多核并发缓存结构&cpu缓存模型
java线程内存模型-JMM
java线程内存模型跟cpu缓存模型类型,是基于cpu缓存模型来建立的,java线程内存模型是标准化的,屏蔽掉了底层不同计算机的区别
public class Volatile {
private static boolean flag = false;
public static void main(String[] args) throws Exception{
new Thread(() -> {
while (!flag){
}
System.out.println(Thread.currentThread().getName() + "============");
}, "线程一").start();
Thread.sleep(1000L);
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "============start");
flag = true;
System.out.println(Thread.currentThread().getName() + "============end");
}, "线程二").start();
}
}
===========输出结果,线程一 一直会空结果等待
线程二============start
线程二============end
主内存与工作内存交互协议
- lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
- unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
- write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中
JMM缓存不一致解决方案
总线锁
cpu从主内存读取数据到高速缓存,会在总线对这个数据加锁,这样其他cpu无法去读和写这个数据,直到这个cpu使用完数据释放锁之后,其它cpu才能读取该数据
MESI缓存一致性协议
多个cpu从主内存读取同一个数据到各自的高速缓存,当其中某个cpu修改了缓存里的数据,改数据会马上同步回主内存,其他cpu通过总线嗅探机制可以感知到数据的变化从而将自己缓存里的数据失效
多核CPU的情况下有多个一级缓存,如何保证缓存内部数据的一致,不让系统数据混乱。这里就引出了一个 一致性的协议MESI。MESI 是指4中状态的首字母。每个Cache line(缓存存储数据的单元)有4个状态,可用2个bit表示,它们分别是
状态 | 描述 | 监听任务 |
---|---|---|
M-修改 modified | 该cache line有效,数据被修改了,和内存中的数据不一致,数据只存在本缓存行中 | 缓存行必须时刻监听所有试图读该缓存行的操作 |
E-独享exclusive | 该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。 | 缓存行也必须监听其它缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S(共享)状态。 |
S-共享shared | 该Cache line有效,数据和内存中的数据一致,数据存在于多个Cache line中。 | 缓存行也必须监听其它缓存行,使该缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效(Invalid) |
I-无效invalid | 该Cache line无效。 | 无 |
注意:对于M和E状态而言总是精确的,他们在和该缓存行的真正状态是一致的,而S状态可能是非一致的。如果一个缓存将处于S状态的缓存行作废了,而另一个缓存实际上可能已经独享了该缓存行,但是该缓存却不会将该缓存行升迁为E状态,这是因为其它缓存不会广播他们作废掉该缓存行的通知,同样由于缓存并没有保存该缓存行的copy的数量,因此(即使有这种通知)也没有办法确定自己是否已经独享了该缓存行。
从上面的意义看来E状态是一种投机性的优化:如果一个CPU想修改一个处于S状态的缓存行,总线事务需要将所有该缓存行的copy变成invalid状态,而修改E状态的缓存不需要使用总线事务。
MESI状态转换
触发事件 | 描述 |
---|---|
本地读取 - local read | 本地cache读取本地cache数据 |
本地写入 - local write | 本地cache写入本地cache数据 |
远端读取 - remote read | 其他cache读取本地cache数据 |
远端写入 - remote write | 其他cache写入本地cache数据 |
本地cache:指当前cpu的cache
触发cache:触发读写事件的cache
其它cache:除了上述两种之外的cache