CAS和ABA问题

✅ CAS 与 ABA 问题及版本号机制详解笔记


一、CAS 是什么?

CAS(Compare-And-Swap) 是一种无锁并发原子操作,用于多线程环境下对共享变量进行安全更新。

方法定义(以 AtomicInteger 为例):

boolean compareAndSet(expectedValue, newValue)
  • 如果当前值 == expectedValue,则将其更新为 newValue

  • 否则更新失败,说明值已经被其他线程修改


二、什么是 ABA 问题?

📌 问题定义:

CAS 在比较当前值和期望值时,只能判断值是否相等,而无法判断值在期间是否被其他线程改过然后又改回来

🧠 举个经典示例:

初始值 A = 100

线程1 读取到 A = 100,准备 CAS 修改为 200
线程2 把 A 改为 150,然后又改回 100

线程1 执行 compareAndSet(100, 200) → 成功

❗问题:虽然值看起来没变,但实际经历了变化 → 潜在的不一致

这就叫做 ABA 问题,即:

A → B → A,但 CAS 只看到“还是 A”

三、为什么 CAS 无法避免 ABA?

  • CAS 只比较“当前值”和“预期值”是否一致

  • 无法知道“值是否在期间变过”

CAS 本质是基于值的等价判断,不具备“变动历史”的感知能力


四、解决 ABA 问题的方式:引入版本号机制

✅ 正确方式:值 + 版本号 分开存储

  • 给每个值增加一个版本号(stamp),每次成功更新都自增版本号

  • CAS 时不仅比较值,还比较版本号


五、带版本号的原子类:AtomicStampedReference<V>

AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 1);

使用示例:

int[] stampHolder = new int[1];
int value = ref.get(stampHolder);  // 获取值和版本号

boolean success = ref.compareAndSet(
    100,              // 期望值
    200,              // 新值
    stampHolder[0],   // 当前版本号
    stampHolder[0] + 1 // 新版本号
);

✅ 只有值和版本号都匹配,CAS 才成功,从而彻底解决 ABA 问题


六、能不能把值 A 直接当成时间戳或版本号用?

不能。值 A 是业务数据,不能直接拿来当版本号
✅ 解决方案是 “值 + 版本号” 分开存储 → 不会破坏原有业务数据


七、扩展:常见变种类对比

类名是否解决 ABA特点
AtomicInteger / AtomicReference只有值,无版本
AtomicStampedReference值 + 整型版本号
AtomicMarkableReference值 + 标志位(boolean),判断是否被修改过
自增值方案(值单调递增)✅(间接)值本身不会回退,所以不会出现 ABA,但场景有限

八、结论总结

  • ABA 本质问题:CAS 看不出值“曾经变过”

  • 根本解决方法:加入版本号或标志位,感知中间变化

  • 实践推荐:使用 AtomicStampedReferenceAtomicMarkableReference,在高并发场景(如锁、队列)中避免 ABA 问题


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值