小陈:老王,上一章你让我看看MESI一致性协议,我大概了解了一下。
老王:哦,来说说你对MESI一致性协议的理解
小陈:MESI协议也叫做缓存一致性协议,主要是用来进行协调多核CPU的高级缓存的数据一致的。 第一章的时候讲过,CPU多级缓存架构,存在多个高速缓存之间数据一致性的问题。
老王:哦,第一章讲的多核CPU高速缓存之间的数据一致性问题,你还记得啊,哈哈,那你来说说。
小陈:当然,当时我可以记在小本本里面的,就等着你来讲这事的呢,沿用第一篇的那个图来讲一下:
(1)看下面的图,假如两个CPU0、CPU1同时将 i = 0 读取进入自己的高速缓存
(2)然后CPU0执行得比较快,执行完 i++操作之后将 i=1 的结果刷回主存了
(3)但是CPU1并不知道数据已经变更了,还是使用 i = 0 的结果去进行 i++ 操作
(4)本来正确的结果应该是 i = 2的,结果执行了两次i++,结果却是1
老王:没错,当时讨论多核CPU的高速缓存的时候,留了这个问题;看来你还记得这个问题,很好很好,掌声鼓励一下!
小陈:嘿嘿,突然的夸我,怪不好意思的......
小陈:造成上面数据不一致的原因主要是因为,CPU0修改了数据之后,没有机制能够通知到CPU1,让CPU1它高速缓存上这个变量的数据失效掉,导致CPU1计算的时候还是使用旧的值。
所以要解决多核CPU的高速缓存数据一致性的问题,必须有个机制能够某个CPU0修改了数据之后,立即通知的CPU1,让CPU1的高速缓存上的这个变量数据失效掉; 然后CPU1用到的时候重新重主内存读取最新的值,这样就能得到最新的结果了。
小陈:MESI实现的缓存一致性协议,正是CPU0修改了数据,通知到CPU1的那套通知机制的一种规范,计算机厂商根据这套规范实现了这种通知机制,但是不同的厂商之间实现方式可能稍微不同。
老王:哎呀呀,不错啊。看来两天不见,你竟然把问题研究得这么深入了。
小陈:那是,问了看懂这个MESI协议,我可是连续肝了两天晚上,点了无数根华子...
老王:孺子可教也,那你继续来说说MESI的缓存一致性的内容
小陈:MESI一致性协议定义了高速缓存中数据的4中状态,分别是:
M(Modified): 修改过的,只有一个CPU能独占这个修改状态,独占的意思是当有一个CPU的高速缓存数据处于这个状态的时候,其它CPU的高速缓存对这个共享的数据均不能操作;高速缓存中的数据发生了更新,需要被刷入主内存中。
E(Exclusive): 独占状态,只有一个CPU能独占这个状态,同样当某个CPU的高速缓存的数据处于这个状态的时候,其它CPU的均不能操作这个共享数据
S(Share):共享的状态,当CPU的高速缓存中的数据这个状态的时候,各个CPU可以并发的对这个数据进行读取
I(Invalid):无效的,意思是当前高速缓存的这个数据已经是无效了或者过期了,不能使用。
老王:那你继续来说说,MESI这四种状态是怎么来解决多核CPU高速缓存数据一直的?
小陈:好,下面我画几张图来i讲一下:
(1)首先像下面的图一样,CPU0、CPU1将共享变量 i = 0 读取,进入自己高速缓存的时候;缓存的状态是S,也就是共享的
(2)然后CPU0要对 i = 0 的变量进行修改操作,在MESI一致性里面大概会经过这些步骤:
(3)CPU0修改数据,刷新回主内存,还会经过这些步骤:
7. CPU0执行 i++ 操作,将 i = 1 的最新结果刷入到高速缓存中,同时将高速缓存的数据状态设为M(修改过的)
(4)CPU1要读取数据操作,发现高速缓存上数据过期了,回经过下面步骤:
9. CPU1发现自己高速缓存上 i = 0 的数据是 Invalid 过期状态,于是从主存重新读取
小陈:上面就是我理解的MESI一致性协议在多核CPU之间进行协调的原理
老王:牛啊,小陈;短短两天时间你理解到这个程度,这理解能力很不错了,颇有我当年的风范啊.....
小陈:......
老王:就MESI一致性协议来说,你理解到这个程度已经可以了;再往下层次就是各个计算机厂商在硬件层面实现的差异了,这个我也不懂了,理解到这里已经非常不错了...
小陈:老王,我记得你跟我说JAVA内存模型的时候,每个线程在JMM中都有自己的工作内存。每个线程的工作内存都有共享变量的一个副本,这多个工作内存的变量副本之间也可能存在数据不一致的情况。
老王:嗯嗯怎了了?
小陈:我回顾了之前JMM的图,是这样子的:
小陈:多个工作内存之间共享变量x的副本,可能数据不一致,这个数据不一致的可见性问题在JMM层次是怎么解决的?
老王:你想想,Java内存模型建立在多核CPU高速缓存之上对不对?
小陈:是的
老王:那JAVA内存模型对应到底层肯定也是操作主内存、操作高速缓存来操作数据的,既然多核CPU的缓存一致性又是通过MESI协议来达到一致性的。所以啊 ,JAVA内存模型底层其实也还是通过MESI一致性来使得一个线程修改了数据,把别的线程的工作内存副本数据弄失效的。
小陈:哦哦,原来JAVA内存模型底层也是通过MESI来使得别的线程的工作内存变量副本失效的啊。
老王:嗯嗯,没错的。
小陈:底层的原理我知道了,那在JAVA编程的层次又是怎么来做的?
老王:那就是下面要讲的文章了,前面的几篇文章和这一篇我们都是讨论在底层原理层次。真正在使用层次来说,volatile、synchronized、cas、锁等都用到了这些底层原理。
老王:下面我们就先来讨论volatile这个关键字,看看它是怎么来保证可见性的?
小陈:好啊,我期待一波......
4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?
5.volatile怎么保证可见性?
6.什么是内存屏障?具有什么作用?
7.volatile怎么通过内存屏障保证可见性和有序性?
8.volatile为啥不能保证原子性?
9.synchronized是个啥东西?应该怎么使用?
10.synchronized底层之monitor、对象头、Mark Word?
11.synchronized底层是怎么通过monitor进行加锁的?
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
13.synchronized怎么保证可见性、有序性、原子性?
15.unsafe类的CAS是怎么保证原子性的?
16.Atomic原子类体系讲解
17.AtomicInteger、AtomicBoolean的底层原理
18.AtomicReference、AtomicStampReference底层原理
19.Atomic中的LongAdder底层原理之分段锁机制
20.Atmoic系列Strimped64分段锁底层实现源码剖析
21.AQS是个啥?为啥说它是JAVA并发工具基础框架?
22.基于AQS的互斥锁底层源码深度剖析
23.基于AQS的共享锁底层源码深度剖析
24.ReentrantLock是怎么基于AQS实现独占锁的?
25.ReentrantLock的Condition机制底层源码剖析
26.CountDownLatch 门栓底层源码和实现机制深度剖析
27.CyclicBarrier 栅栏底层源码和实现机制深度剖析
28.Semaphore 信号量底层源码和实现机深度剖析
29.ReentrantReadWriteLock 读写锁怎么表示?
31.CopyOnAarrayList 底层分析,怎么通过写时复制副本,提升并发性能?
32.ConcurrentLinkedQueue 底层分析,CAS 无锁化操作提升并发性能?
33.ConcurrentHashMap详解,底层怎么通过分段锁提升并发性能?
34.LinkedBlockedQueue 阻塞队列怎么通过ReentrantLock和Condition实现?
35.ArrayBlockedQueued 阻塞队列实现思路竟然和LinkedBlockedQueue一样?
36.DelayQueue 底层源码剖析,延时队列怎么实现?
37.SynchronousQueue底层原理解析
39.ThreadPoolExecutor 构造函数有哪些参数?这些参数分别表示什么意思?
40.内部有哪些变量,怎么表示线程池状态和线程数,看看道格.李大神是怎么设计的?