15.unsafe类的CAS是怎么保证原子性的?

  • Post author:
  • Post category:其他


老王:小陈啊,


上一章


我们讲了

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并发文章目录(公众号)

JAVA并发专题 《筑基篇》


1.什么是CPU多级缓存模型?


2.什么是JAVA内存模型?


3.线程安全之可见性、有序性、原子性是什么?


4.什么是MESI缓存一致性协议?怎么解决并发的可见性问题?

JAVA并发专题《练气篇》


5.volatile怎么保证可见性?


6.什么是内存屏障?具有什么作用?


7.volatile怎么通过内存屏障保证可见性和有序性?


8.volatile为啥不能保证原子性?


9.synchronized是个啥东西?应该怎么使用?


10.synchronized底层之monitor、对象头、Mark Word?


11.synchronized底层是怎么通过monitor进行加锁的?


12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁


13.synchronized怎么保证可见性、有序性、原子性?

JAVA并发专题《结丹篇》


14. JDK底层Unsafe类是个啥东西?


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 读写锁怎么表示?

30. ReentrantReadWriteLock 读写锁底层源码和机制深度剖析

JAVA并发专题《元神篇》并发数据结构篇

31.CopyOnAarrayList 底层分析,怎么通过写时复制副本,提升并发性能?

32.ConcurrentLinkedQueue 底层分析,CAS 无锁化操作提升并发性能?

33.ConcurrentHashMap详解,底层怎么通过分段锁提升并发性能?

34.LinkedBlockedQueue 阻塞队列怎么通过ReentrantLock和Condition实现?

35.ArrayBlockedQueued 阻塞队列实现思路竟然和LinkedBlockedQueue一样?

36.DelayQueue 底层源码剖析,延时队列怎么实现?

37.SynchronousQueue底层原理解析

JAVA并发专题《飞升篇》线程池底层深度剖析

38. 什么是线程池?看看JDK提供了哪些默认的线程池?底层竟然都是基于ThreadPoolExecutor的?

39.ThreadPoolExecutor 构造函数有哪些参数?这些参数分别表示什么意思?

40.内部有哪些变量,怎么表示线程池状态和线程数,看看道格.李大神是怎么设计的?

41. ThreadPoolExecutor execute执行流程?怎么进行任务提交的?addWorker方法干了啥?什么是workder?

42. ThreadPoolExecutor execute执行流程?何时将任务提交到阻塞队列? 阻塞队列满会发生什么?

43. ThreadPoolExecutor 中的Worker是如何执行提交到线程池的任务的?多余Worker怎么在超出空闲时间后被干掉的?

44. ThreadPoolExecutor shutdown、shutdownNow内部核心流程

45. 再回头看看为啥不推荐Executors提供几种线程池?

46. ThreadPoolExecutor线程池篇总结



版权声明:本文为chenzengnian123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。