老王:小陈啊,上一章我们讲了usafe是个啥东西,以及unsafe提供的几大类的功能
老王:这一章啊,我们要花个时间专门讲unsafe提供的cas功能,这个cas的功能是我们后面将Atomic原子类体系的基础。
小陈:cas功能?上一章的时候不是已经介绍过了吗?
老王:上一章只是简单的介绍了一下CAS功能而已,但是关于unsafe的cas功能底层是怎么保证原子性的?在操作系统层面是怎么实现的? 这些东西我们还没有讲。
由于的后面的并发知识非常多的使用到了unsafe的cas功能,所以啊,我们今天专门花一章的时间,来把CAS底层的原理弄懂
小陈:哦哦,原来是这样啊......
老王:上一章我们讲解CAS操作的时候,是直接通过( 对象地址 + 对象内部属性偏移量offset ) 直接定位到要修改的变量在内存的位置,然后在内存级别的比较数据和修改数据。也就是像下面的图一样:
操作的时候直接根据 o对象地址 + offset偏移量地址,定位到demo属性在内存的位置,然后直接操作内存修改数据。
老王:由于CPU是不会直接读写主存的,数据读取的时候还是先将数据读取到高速缓存,然后通过高速缓存传递给CPU;写数据的时候也是先高速缓存,然后再将高速缓存的数据写入内存;于是可以得到下面的图形:
老王:小陈啊,想想一下上面的那副图,如果在多线程并发操作的时候会有什么问题?
小陈:多个线程或者多个CPU同时读取和修改demo属性的时候,可能会导致数据不一致的问题,比如我拿一个 i++ 的例子来说:
比如CPU0、CPU1都通过内存地址定位到 i 所在位置,然后同时读取 i = 0,然后同时执行i++ 操作,再刷新会主内存,这个时候就会导致 i 的值不是我们想要的
老王:你说的没错,如果在多个CPU都可以同时操作一个共享变量的时候,就会出现你说的这个问题。
小陈:我记得CAS操作是可以保证原子性的,也就是同一个时间,同一个操作只允许一个CPU操作成功,它这个又是怎么保证的呢?
老王:这个啊,其实CAS底层的操作,还是会用到锁的!!! ,只不过这个锁是比较轻量级的,不会导致线程沉睡,下面我来讲讲CAS加锁来保证原子性的原理。
CAS底层使用锁保证原子性
老王:说起CAS操作啊,我还是画图给你比较好讲一点:
(1)首先CPU0要执行CAS操作对变量 i 进行赋值,然后CPU0告诉总线说我要申请单独操作变量 i 的权限,帮我告诉一下CPU1等其它的CPU兄弟
(2)总线通知到了CPU1,CPU1告诉总线,好的,我不会操作数据,让CPU0大胆的去操作吧
(3)然后总线告诉CPU0,你可以独占变量 i 的操作了,其它的兄弟表示不会干扰你
(4)然后CPU0从自己的缓存读取 变量 i 的值;然后又根据 (o对象地址 + offset偏移量地址) 直接定位到变量 i 在内存的位置,直接读取变量 i 在内存的值
(5)接下来的操作就简单了,由于不会有人干扰,直接对比缓存的值和内存的值是否一致就可以了,如果一致,我直接修改,然后刷回主内存;如果不一致,说明我本地的数据不是最新的,需要重新申请CAS操作。
老王:小陈啊,这个就是CAS在底层操作的原理,它底层还是通过加锁来保证原子性的,同一个时间只能有一个CPU能申请到CAS的操作权限,你理解了吗?
小陈:哈哈,老王,你画的这个图太好了,我看到图就知道它是怎么操作的了,真牛啊......
老王:好的,那这一张CAS底层加锁保证原子性的讨论我们就到这里了,我们明天继续...
小陈:好的,老王......
老王:我们从下一章开始,就开始进入JUC提供的Atomic原子类的学习了......
小陈:那我们下一章见。