CAS技术实现之底层原理

2025-03-04 21:46:3889 次浏览

最佳答案

小伙伴们都知道,i++其实并非是原子性操作,在多线程环境下会有线程安全的问题,下面我们来写个测试demo来验证这条结论。

通过上面的小例子我们可以发现每次计算的结果都有偏差。为什么会存在偏差呢?这是因为JMM将内存分为工作内存+主内存。我们的运算工作是在工作内存中进行,然后再将得到的值同步到主内存中。

通过上图我们可以看到,一开始主内存中i=0,此时线程A把i读到工作内存,并开始进行i++的运算,然后把运算结果i=1同步给主内存。但是因为整个过程并不是原子性的(线程A运算的过程中,线程B也可以进行运算),这时候线程A还没有来得及把计算后的值刷新回主内存,线程B就开始进行了i++的操作,此时线程B拿到的i的值为0,而不是线程A计算后的1,这样线程B经过运算,得到的结果也是1,这样就导致最终结果是1而不是我们期望的2,从而造成线程安全问题。

1.synchronized锁

当我们对i++加了synchronized锁后,就可以保证它具有原子性,从而保证同一时刻只有一个线程能对i进行++操作,进而保证线程安全。

通过synchronized锁后,得到的结果跟预期结果相符。synchronized底层原理不是本篇文章的重点,后面会单出一篇文章来进行剖析。

2.通过J.U.C包下的AtomicInteger

ok,前面铺垫那么多,现在正式引入本文的重点: CAS ,AtomicInteger就是基于CAS技术实现的。

CAS,Compare and Swap即比较并替换。它是乐观锁思想的一种实现方式。

通过图我们可以看出CAS实现原理:CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做并返回false。

1.点开getAndIncrement()方法,我们会发现AtomicInteger调用了Unsafe的getAndInt()方法

小知识: JVM是规范,目前市面上主要有四种实现

1)Hotspot:最常用的jvm实现

2)JRocket:JRocket是BEA公司的JVM.使用WebLogic的用户,往往使用JRocket虚拟机.

3)J9:IBM公司的JVM

4)Harmony:IBM和Intel搞的开源JVM. IBM牵头,主力是Intel.

找到 atomic_linux_x86.inline.hpp ,找到cmpxchg方法

CAS是一种乐观锁,采用自旋的方式来等待其他线程完成工作。在竞争比较低且等待时间短的任务场景中表现优异。

1)因为CAS采用自旋方式,而自旋是需要占用CPU资源的。

2)只能保持一个变量的原子操作

3) ABA问题

这里我来给小伙伴解释下什么是ABA问题,还是拿CAS流程图来讲

前面我们也提到了,CAS虽然会占用CPU资源,但是只在用户态就可以完成加锁的过程(不需要涉及到内核态)。那么线程数较少,竞争不激烈,等待时间短的场景就是CAS的最佳适用场景。小伙伴们get到了吗?

声明:知趣百科所有作品均由用户自行上传分享,仅供网友学习交流。若您的权利被侵害,请在页面底部查找“联系我们”的链接,并通过该渠道与我们取得联系以便进一步处理。