CAS学习
1. CAS 是什么?
CAS 全称:Compare And Swap(比较并交换)
简单说就是:比较内存里的值和预期值是否一样,一样就更新,不一样就重试。
Java 中的 AtomicInteger 就是用 CAS 实现的,多个线程同时执行 CAS 操作,只有一个能成功,这是硬件保证的。


2. CAS 底层怎么实现的?
CAS 是个乐观锁,它假设不会有冲突,直接去改,改不了就重试。
在 CPU 层面,CAS 是通过 cmpxchg 指令实现的。但这个指令本身不是原子的,可能会被打断:
比如线程 A 正在执行 cmpxchg,发现内存值和预期值一样,准备写入新值,这时候线程 B 突然插进来把值改了,就出问题了。
怎么办?在 cmpxchg 前面加个 lock 前缀指令,通过总线锁或缓存锁来保证原子性。
所以虽然我们说 CAS 是"无锁算法",但底层其实还是加了锁的,只不过锁的粒度很小。
3. CAS 有什么问题?
3.1 ABA 问题
线程 1 读到值是 A,正在计算新值的时候,线程 2 把值从 A 改成 B,又改回 A。
这时候线程 1 用 CAS 去更新,发现值还是 A,就以为没人动过,其实已经被改过了。
什么时候会出现?
变量的值可以来回变(A → B → A)就会有这个问题。如果值只能往一个方向变(比如自增的 ID),就不会有。
怎么解决?
给变量加上版本号或时间戳,不光比较值,还比较版本号。
3.2 CPU 开销大
如果 CAS 一直失败,就会不停地循环重试,白白浪费 CPU。
高并发场景下,大量线程都在自旋,CPU 就被耗光了。
3.3 只能保证一个变量的原子性
CAS 只能对单个变量进行原子操作,多个变量就不行了。
怎么办?
Java 提供了 AtomicReference 类,可以把多个变量封装成一个对象,然后对这个对象用 CAS。