大家好,我是Java不惑(WX公众号同名)。这是专栏的第二篇文章,我将给大家简单介绍一下volatile和cas的原理。
为什么说简单介绍,因为不同的处理器有不同的实现方式,并且处理器过于复杂,我们只需要简单了解就可以了。
在这篇文章中,我将向大家介绍缓存一致性协议,并介绍缓存一致性协议是怎样实现可见性和有序性。
对volatile修饰的变量,编译后的指令增加了lock指令的前缀:
lock add1 $0x0,(%esp)
CAS编译后,也会自动增加lock前缀。
lock cmpxchg
正是加了lock前缀,才让volatile修饰符具有可见性和有序性,也让cas可以原子的替换变量。
那么lock指令究竟让处理器做了什么操作呢?
在上一篇文章中,我们知道了内核之间是可以通信的,那么内核之间是怎么通信的呢?
带着疑问,我们继续向下看。
Lock指令在处理器中有两种处理实现方式:总线锁和缓存一致性协议。
总线锁顾名思义,就是锁住总线。CPU总线负责CPU和外部(高速缓存、内存等)通信,使用总线锁会选择一个核心独占总线,其他内核不能和内存通信。
此时其它内核无法通信,该内核独享共享内存,也就解决了原子性问题。
但是内核无法通信时,开销比较大,在最初的处理器中提供这种方式,现在又提出了缓存一致性协议。
现在处理器有专门的协议来解决多核缓存中数据一致性问题,比较经典的有MESI协议,下面我们主要介绍MESI协议。
上一篇文章中我们介绍过缓存行,在MESI的实现中,每个缓存行包含三部分:vaild、tag和block,三部分介绍如下:
vaild是校验字段,我们能通过这个字段去判断缓存行是否是可用的。对于vaild字段有两种处理方式:
MESI协议使用的是写失效的思路。在MESI协议中,缓存行valid有四种状态:
1、 M(Modified,被修改):缓存行中的数据只缓存在该内核的缓存中,并被修改过。当前缓存行中的数据和主存中的数据不一致,需要在某个时间写回到主存中。
2、 E(Exclusive,独享):该缓存行只被缓存在该内核的缓存中,并且是未被修改的,与主存中的数据一致。当其他内核读取该缓存行时变为共享状态。
3、 S(Shared,共享):该缓存行可能被多个内核缓存,并且各缓存行中的数据和主数据一致,当有一个CPU修改数据时,其他内核中该缓存行中的数据被设置为无效状态。
4、 I(Invalid,无效):该缓存行无效
MESI协议要求缓存没有命中时,允许缓存在其他缓存中复制数据,所以减少了读取主存。
首先我们看一下内核修改数据后,会怎么处理,缓存行中的数据会怎么变化。
为了保证各个内核读取的data共享变量是最新的,core0 需要等到core1返回消息后才能继续执行。
但是内核0不可能等待接收到内核1的消息之后才进行后续操作。
所以新增了一个存储缓存的组件。
core0广播消息时,先将消息保存到StoreBuffer中,这样core0就不必知道其他内核是否已经接收到消息。
内核中引入Store Buffer解决了内核之间等待的问题,但是又引入了Store Buffer和Cache之间数据同步的问题。
为此,针对Store Buffer,CPU在后续变量新值写入之前,把Store Buffer的所有值按顺序刷新到内存中。
这就称为内存屏障中的写屏障(Store Barrier)
core0广播数据修改之后,core1不可能马上处理,而是在内核中新增一个无效队列的组件,用于存放接收广播中的无效数据。
上面我们介绍了总线锁和缓存一致性,使用lock前缀的指令,内核会使用缓存一致性协议来处理共享数据。
内存屏障有读屏障、写屏障和全屏障(full barrier)等几种。
读屏障:获取其他内核修改,让当前内核中的数据为最新的值,也就是将Invalidate Queue中的数据应用到内核;
写屏障:将内核的修改让其他内核可见,将storeBuffer的数据写入缓存/内存中;
全屏障:是几种内存屏障里面开销最大的,包含了其他几种屏障;
因为有缓存一致性和总线锁,所以volatile实现了可见性。
同时lock具有full barrier的效果,所以volatile保证了有序性。
在这篇文章中,我介绍了缓存一致性和volatile的关系,以及volatile是怎样实现可见性和有序性的。希望你看完有所收获,受限于个人水平,文章若有错漏,还望读者不吝赐教。
在下一篇文章中,我将向大家介绍Synchronized和Reentrantlock背后的设计思想,也就是大学教材《计算机操作系统》中的信号量和管程。
最后,如果我的文章对你有帮助,请帮我点赞转发!
如果你对volatile和cas不熟悉,可以看我的第一篇文章《【刨根问底】带你深入理解JUC并发工具类 — volatile和cas》
或者直接访问该专栏的导航文章:《【刨根问底】带你深入理解JUC并发工具类 — 开篇》