老王:小陈啊,上一章 我们讲了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原子类的学习了......
小陈:那我们下一章见。
目录
JAVA并发专题 《筑基篇》
4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?
JAVA并发专题《练气篇》
10.synchronized底层之monitor、对象头、Mark Word?
11.synchronized底层是怎么通过monitor进行加锁的?
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
13.synchronized怎么保证可见性、有序性、原子性?
JAVA并发专题《结丹篇》
15.unsafe类的CAS是怎么保证原子性的?
16.Atomic原子类体系讲解
17.AtomicInteger、AtomicBoolean的底层原理
18.AtomicReference、AtomicStampReference底层原理
19.Atomic中的LongAdder底层原理之分段锁机制
20.Atmoic系列Strimped64分段锁底层实现源码剖析
JAVA并发专题《金丹篇》
21.AQS是个啥?为啥说它是JAVA并发工具基础框架?
22.基于AQS的互斥锁底层源码深度剖析
23.基于AQS的共享锁底层源码深度剖析
24.ReentrantLock是怎么基于AQS实现独占锁的?
25.ReentrantLock的Condition机制底层源码剖析
26.CountDownLatch 门栓底层源码和实现机制深度剖析
27.CyclicBarrier 栅栏底层源码和实现机制深度剖析
28.Semaphore 信号量底层源码和实现机深度剖析
29.ReentrantReadWriteLock 读写锁怎么表示?
- ReentrantReadWriteLock 读写锁底层源码和机制深度剖析
JAVA并发专题《元神篇》并发数据结构篇
31.CopyOnAarrayList 底层分析,怎么通过写时复制副本,提升并发性能?
32.ConcurrentLinkedQueue 底层分析,CAS 无锁化操作提升并发性能?
33.ConcurrentHashMap详解,底层怎么通过分段锁提升并发性能?
34.LinkedBlockedQueue 阻塞队列怎么通过ReentrantLock和Condition实现?
35.ArrayBlockedQueued 阻塞队列实现思路竟然和LinkedBlockedQueue一样?
36.DelayQueue 底层源码剖析,延时队列怎么实现?
37.SynchronousQueue底层原理解析
JAVA并发专题《飞升篇》线程池底层深度剖析
- 什么是线程池?看看JDK提供了哪些默认的线程池?底层竟然都是基于ThreadPoolExecutor的?
39.ThreadPoolExecutor 构造函数有哪些参数?这些参数分别表示什么意思?
40.内部有哪些变量,怎么表示线程池状态和线程数,看看道格.李大神是怎么设计的?
-
ThreadPoolExecutor execute执行流程?怎么进行任务提交的?addWorker方法干了啥?什么是workder?
-
ThreadPoolExecutor execute执行流程?何时将任务提交到阻塞队列? 阻塞队列满会发生什么?
-
ThreadPoolExecutor 中的Worker是如何执行提交到线程池的任务的?多余Worker怎么在超出空闲时间后被干掉的?
-
ThreadPoolExecutor shutdown、shutdownNow内部核心流程
-
再回头看看为啥不推荐Executors提供几种线程池?
-
ThreadPoolExecutor线程池篇总结