什么是CAS
CAS=Compare And Swap,意思是等值比较交换,比较内存中的值和预期值,如果相等就更新为新值。整个过程是原子的,不会被其他线程打断。在 程序中体现是无锁化的,步骤就是:读取内存值、比较、交换;整个一体为原子性。
CAS原理:
CAS只是在应用上体现无锁,在多核CPU操作下,会通过硬件锁控制,在锁的底层其实系统是对这些操作是有加锁的,如下:
- x86 架构:使用
LOCK CMPXCHG指令,通过总线锁或缓存锁机制确保操作期间内存访问的独占性。 - ARM 架构:通过
LDREX(加载并标记独占访问)和STREX(尝试存储并解锁)指令组合实现原子性。
操作流程
CAS 操作包含三个不可分割的步骤:
- 读取内存值:从内存地址
V中读取当前值。 - 比较预期值:将读取的值与预期值
A进行比较。 - 条件更新:若相等,则将内存值更新为新值
B;否则放弃操作
这三个步骤在硬件层面作为一个整体执行,不会被线程切换或中断打断。
CAS注意点
- 一般CAS如果遇到并发是需要自旋重试的,CAS并不适合在多线程场景竞争资源非常激烈的情况操作,否则会一直自旋操作。
- CAS可能会出现ABA的问题,一般都是通过版本号来控制。
有什么场景使用到原子操作?
原子类在 sun.misc.Unsafe里边有很多原子操作,CAS就是在这里边如compareAndSwapInt。
- 并发容 器
- ConcurrentHashMap 1.8的一个空桶初始化,使用CAS操作初始化桶。
- ConcurrentLinkedQueue 无锁的入队出队操作。
- 原子类:
AtomicInteger、AtomicLong
- 一般用来多线程的一些计数统计,如整个web应用的访问量,多线程的操作次数等。
- AQS 框架
- AbstractQueuedSynchronizer(AQS)通过 CAS 操作,同步state状态字段,从而支撑ReentrantLock等。
开发场景
- 统计接口访问量、用户点击量等高频操作;Web 服务统计、API 调用量监控
- 线程启动/停止控制
通过 AtomicBoolean实现无锁状态切换:线程池任务调度
AtomicBoolean isRunning = new AtomicBoolean(false);
public void startTask() {
if (isRunning.compareAndSet(false, true)) {
// 启动任务
}
}
- 对象字段原子更新
对特定字段(如用户积分字段更新)进行原子操作:
class User {
private AtomicInteger score = new AtomicInteger(0);
public void addScore(int points) {
score.addAndGet(points);
}
}