当前位置: 首页 > news >正文

Java EE初阶启程记11---CAS

 🔥个人主页:寻星探路

🎬作者简介:Java研发方向学习者

📖个人专栏: 、《

⭐️人生格言:没有人生来就会编程,但我生来倔强!!!



目录

一、什么是CAS

1、CAS伪代码

二、CAS是怎么实现的

三、CAS有哪些应用

1、实现原子类

2、实现自旋锁

四、CAS的ABA问题

1、什么是ABA问题

2、ABA问题引来的BUG

3、解决方案

五、相关面试题


一、什么是CAS

CAS:全称Compareandswap,字面意思:“比较并交换”,一个CAS涉及到以下操作:

我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。

1)比较A与V是否相等。(比较)

2)如果比较相等,将B写入V。(交换)

3)返回操作是否成功。

1、CAS伪代码

        下面写的代码不是原子的,真实的CAS是一个原子的硬件指令完成的,这个伪代码只是辅助理解CAS 的工作流程。

 boolean CAS(address, expectValue, swapValue) {if (&address == expectedValue) {&address = swapValue;return true;}return false;}

两种典型的不是"原子性"的代码

1)checkandset(if 判定然后设定值)[上面的CAS伪代码就是这种形式]

2)readandupdate(i++)[之前我们讲线程安全的代码例子是这种形式]

        当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。

CAS可以视为是一种乐观锁。(或者可以理解成CAS是乐观锁的以种实现方式)

二、CAS是怎么实现的

针对不同的操作系统,JVM⽤到了不同的CAS实现原理,简单来讲:

        java的CAS利用的的是unsafe这个类提供的CAS操作;

        unsafe的CAS依赖了的是jvm针对不同的操作系统实现的Atomic::cmpxchg;

        Atomic::cmpxchg的实现使用了汇编的CAS操作,并使用cpu硬件提供的lock机制保证其原子 性。

简而言之,是因为硬件予以了支持,软件层面才能做到。

三、CAS有哪些应用

1、实现原子类

        标准库中提供了 java.util.concurrent.atomic 包,里面的类都是基于这种方式来实现的,典型的就是AtomicInteger类,其中的getAndIncrement相当于i++操作。

 AtomicInteger atomicInteger = new AtomicInteger(0);// 相当于 i++ atomicInteger.getAndIncrement();

伪代码实现:

 class AtomicInteger {private int value;public int getAndIncrement() {int oldValue = value;while ( CAS(value, oldValue, oldValue+1) != true) {oldValue = value;}return oldValue;}}

假设两个线程同时调用getAndIncrement

1)两个线程都读取value的值到oldValue中。(oldValue是一个局部变量,在栈上,每个线程有自己的栈)

2)线程1先执行CAS操作,由于oldValue和value的值相同,直接进行对value赋值。

#注:

        CAS是直接读写内存的,而不是操作寄存器。

        CAS的读内存,比较,写内存操作是一条硬件指令,是原子的。

3)线程2再执行CAS操作,第一次CAS的时候发现oldValue和value不相等,不能进行赋值,因此需要进入循环。

        在循环里重新读取value的值赋给oldValue

4)线程2接下来第⼆次执行CAS,此时oldValue和value相同,于是直接执行赋值操作。

5)线程1和线程2返回各自的oldValue的值即可。

        通过形如上述代码就可以实现一个原子类,不需要使用重量级锁,就可以高效的完成多线程的自增操作。

        本来checkandset这样的操作在代码角度不是原子的,但是在硬件层面上可以让一条指令完成这个操作,也就变成原子的了。

2、实现自旋锁

基于CAS实现更灵活的锁,获取到更多的控制权。

自旋锁伪代码

 public class SpinLock {private Thread owner = null;public void lock(){// 通过 CAS 看当前锁是否被某个线程持有.  // 如果这个锁已经被别的线程持有, 那么就⾃旋等待.  // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程.while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){this.owner = null;}}

四、CAS的ABA问题

1、什么是ABA问题

ABA的问题:

假设存在两个线程t1和t2,有一个共享变量num,初始值为A。

接下来,线程t1想使用CAS把num值改成Z,那么就需要

        先读取num的值,记录到oldNum变量中。

        使用CAS判定当前num的值是否为A,如果为A,就修改成Z。

但是,在t1执行这两个操作之间,t2线程可能把num的值从A改成了B,又从B改成了A

        线程t1的CAS是期望num不变就修改,但是num的值已经被t2给改了,只不过又改成A了,这个时候t1究竟是否要更新num的值为Z呢?

        到这一步,t1线程无法区分当前这个变量始终是A,还是经历了一个变化过程。

        这就好比,我们买一个⼿机,无法判定这个手机是刚出厂的新⼿机,还是别人用旧了,又翻新过的手机。

2、ABA问题引来的BUG

        大部分的情况下,t2线程这样的一个反复横跳改动,对于t1是否修改num是没有影响的,但是不排除一些特殊情况。   

假设滑稽老哥有100存款,滑稽想从ATM取50块钱,取款机创建了两个线程,并发的来执行-50操作。

我们期望一个线程执行-50成功,另一个线程-50失败。

如果使用CAS的方式来完成这个扣款过程就可能出现问题。

正常的过程:

1)存款100,线程1获取到当前存款值为100,期望更新为50;线程2获取到当前存款值为100,期望更新为50。

2)线程1执行扣款成功,存款被改成50,线程2阻塞等待中。

3)轮到线程2执行了,发现当前存款为50,和之前读到的100不相同,执行失败。

异常的过程:

1)存款100,线程1获取到当前存款值为100,期望更新为50;线程2获取到当前存款值为100,期望更新为50。

2)线程1执行扣款成功,存款被改成50,线程2阻塞等待中。

3)在线程2执行之前,滑稽的朋友正好给滑稽转账50,账户余额变成100!!

4)轮到线程2执行了,发现当前存款为100,和之前读到的100相同,再次执行扣款操作。

这个时候,扣款操作被执行了两次!!!都是ABA问题搞的贵!!!

3、解决方案

给要修改的值,引入版本号,在CAS比较数据当前值和旧值的同时,也要比较版本号是否符合预期

        CAS操作在读取旧值的同时,也要读取版本号。

        真正修改的时候:

                如果当前版本号和读到的版本号相同,则修改数据,并把版本号+1。

                如果当前版本号高于读到的版本号,就操作失败(认为数据已经被修改过了)。

        这就好比,判定这个手机是否是翻新机,那么就需要收集每个手机的数据,第一次挂在电商网站上的手机记为版本1,以后每次这个手机出现在电商网站上,就把版本号进行递增。这样如果买家不在意这是翻新机,就买,如果买家在意,就可以直接略过。

对比理解上面的转账例子

假设滑稽老哥有100存款,滑稽想从ATM取50块钱,取款机创建了两个线程,并发的来执行-50操作。

我们期望一个线程执行-50成功,另一个线程-50失败。

为了解决ABA问题,给余额搭配一个版本号,初始设为1。

1)存款100,线程1获取到存款值为100,版本号为1,期望更新为50;线程2获取到存款值为100,版本号为1,期望更新为50。

2)线程1执行扣款成功,存款被改成50,版本号改为2,线程2阻塞等待中。

3)在线程2执行之前,滑稽的朋友正好给滑稽转账50,账户余额变成100,版本号变成3。

4)轮到线程2执行了,发现当前存款为100,和之前读到的100相同,但是当前版本号为3,之前读到的版本号为1,版本小于当前版本,认为操作失败。

        在Java标准库中提供了 AtomicStampedReference 类,这个类可以对某个类进行包装,在内部就提供了上面描述的版本管理功能。

关于 AtomicStampedReference 的具体⽤法此处不再展开,有需要的同学自行查找文档了解使用方法即可

五、相关面试题

1)讲解下你自己理解的CAS机制

全称Compareandswap,即"比较并交换",相当于通过一个原子的操作,同时完成"读取内存,比较是否相等,修改内存"这三个步骤,本质上需要CPU指令的支撑。

2)ABA问题怎么解决?

给要修改的数据引入版本号,在CAS比较数据当前值和旧值的同时,也要比较版本号是否符合预期,如果发现当前版本号和之前读到的版本号一致,就真正执行修改操作,并让版本号自增;如果发现当前版本号比之前读到的版本号大,就认为操作失败。

http://www.dtcms.com/a/450749.html

相关文章:

  • 在大型游戏中调试中介系统:我的 LLM 对比与思考
  • Linux网络--5、传输层协议
  • 深圳开发网站开发费用网络营销推广方法研究
  • 手机网站开发项目软件详细设计文档
  • Java中的并发工具类CountDownLatch,CyclicBarrier,Semaphore
  • 《工业边缘网关进阶指南:智慧工厂设备互联中的协议适配与数据预处理》
  • 十大免费文案网站口碑营销的模式
  • 个人免费设计网站活动策划案模板
  • 免费网站推广产品vue做的个人网站
  • 玉溪网站制作网站二级目录做优化
  • 白天/夜间野生动物检测识别数据集:近1w图像,5类,yolo标注
  • 山东省南水北调建设管理局网站网站空间控制
  • 温州网站制作价格做移动网站多少钱
  • 【LaTeX】 12 LaTeX 参考文献管理
  • 【郑州网站建设】网站群建设方案
  • 整站seo哪家服务好WordPress音乐主题模版 Musik
  • P5490 【模板】扫描线 矩形面积并
  • 网站皮肤样板如何打开网页
  • 电子商务网站建设 教学大纲中装建设市值
  • 做企业门户网站都免费建站平台哪个稳定
  • 网页设计的网站wordpress调用描述
  • 最新文生图模型进展:从Stable Diffusion 3到Sora的视觉革命
  • wap网站还用吗wordpress响应式主题
  • 江苏电商网站开发昆明网站搭建网站运营
  • 自动生成图片的网站商城模板网站模板
  • 湛江市微信网站建设企业中国互联网域名注册服务机构
  • 出国做博后关注哪些网站新冠北京最新消息
  • 做时尚网站的目的网站设计方式
  • 腾讯云建网站pc端网站未来
  • [Dify] 构建“公司内部制度”问答机器人:从知识导入到上线实战指南