【Java】CAS

CAS

CAS:是 一条 特殊的 CPU 指令,其所做的工作就是 “比较和交换”。

CAS有3个操作数,内存值V,预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。当多个线程同时尝试使用CAS更新一个变量时,任何时候只有一个线程可以更新成功,若更新失败,线程会重新进入循环再次进行尝试。

CAS的巧妙之处:

  • 之前的线程安全问题是:两个线程从内存中读取同一个值,导致操作覆盖。
  • CAS能确保内存里面的那个值有没有被修改不变才会进行操作,变了的话就会重新读取内存的值。

CAS应用

1)实现原子类

这是Java标准库提供的基于CAS实现的原子类
![[Pasted image 20240115142753.png]]

包底下有如下实现的原子类:
红框标注的是在开发中常用的原子类,其作用:就是对某一变量进行 ++ / -- 操作
![[Pasted image 20240115142853.png]]


2)实现自旋锁

CAS实现自旋锁的伪代码:
owner 不为 null 的时候,循环就会一直执行下去,通过不断循环来实现 等待 效果

在这里插入图片描述


CAS的ABA问题

CAS 在使用的时候,其关键部分就是:判定当前内存中的值是否是和寄存器中的值是一样的;如果是一样的,则进行修改;如果不一样,则不进行修改。

这里的“一样”,本质上是判定当前这个代码执行过程中,是否有其他线程穿插进来将变量给修改

但是存在这种情况:比如初始时数值为0,在执行CAS之前,另一个线程把这个值从 0 改为了 100,而后又将 100 改为了 0。CAS是无法判断这种情况的

一般情况下是不会有问题的。

但能挑出来说,那就是不出意外的要出意外了:

如:在取钱的场景下,初始情况下账户余额有1000元,现要取500元。但在取钱的时候,ATM卡住了,取钱按钮按第一次没反应,又按了一下。
ATM中就创建了两个线程 t1t2 来尝试进行扣款操作,该场景下期望其中一个线程操作成功,另一个操作失败。此处假定使用CAS的方法来扣款:

如图:t1 在执行完第一条指令后,t2 穿插了进来提前执行完整个指令,此时余额已经发生了变化,再轮到 t1 执行第二条指令时,因为余额发生了变化,t2 第二条指令就不会执行,直接跳出。在这种场景下,是没有问题的
![[Pasted image 20240115145538.png]]

但如若此时又穿插进来一个 t3 线程,给余额充值了 500,CAS是无法判断这种情况的,那么余额就会莫名少了 500
![[Pasted image 20240115145933.png]]


解决 ABA 问题方案
  1. 约定数据变化必须是单向的(只能增加或减少),不可以是双向的(不能既增加又可减少)
  2. 对于本身就必须双向变化的数据,可以给它引入一个版本号(版本号是只能增加不能减少的)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行舟Yi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值