CAS乐观锁适合读多写少场景,因读操作无锁并行、写操作冲突概率低;典型应用包括AtomicInteger、数据库版本号及AtomicReference自定义逻辑,需注意重试限制、ABA问题和写操作精简。

Java 中 CAS 乐观锁特别适合高并发、读多写少的场景,关键在于它把“加锁”这个开销从读操作里彻底拿掉了——读不阻塞、不等待、不竞争,只在真正要写的时候做一次轻量级校验。
为什么 CAS 在读多写少时表现优异
读操作完全无锁,多个线程可并行执行;写操作虽需校验,但因冲突概率低,几乎每次都能一击成功,避免了传统锁的上下文切换和排队等待。比如一个配置中心的开关字段,每秒被读取上万次,但一天只更新几次,CAS 就能扛住这种流量而不抖动。
CAS 的典型落地方式
- AtomicInteger / AtomicLong 等原子类:底层直接调用 Unsafe#compareAndSwapInt,适用于计数器、ID 生成、状态标记等简单值变更。
- 数据库版本号(version 字段):查询时带出 version,更新时 WHERE version = ?,失败则重试或抛异常,适合业务逻辑较重、需事务保障的场景。
- 自定义 CAS 逻辑(如基于 volatile + 循环校验):当需要复合判断(如“只有状态为 A 且 version 为 N 才能更新为 B”)时,可用 AtomicReference + compareAndSet 配合对象封装实现。
必须注意的几个实操细节
- 重试不是无限循环:尤其在数据库版本号场景中,应限制最大重试次数(如 3 次),避免因长事务或网络延迟导致雪崩式重试。
- ABA 问题要规避:单纯用 int 做版本号可能被绕过(A→B→A),推荐用 Long 类型时间戳、或 AtomicStampedReference 带版本戳的引用类型。
- 写操作别裹太多逻辑:CAS 是瞬间动作,若校验通过后还要查库、发消息、调远程接口,会拉长“检查-提交”窗口,增加冲突概率——应把非核心逻辑挪到 CAS 成功后再异步处理。
和悲观锁对比的关键取舍点
不是“CAS 更好”,而是“更匹配”。同一套点赞功能,用乐观锁(CAS 更新 count)能轻松支撑每秒 5 万读 + 200 写;若换成 select for update,数据库连接池很快打满,TP99 延迟飙升。但反过来,如果是秒杀扣库存这种写密集场景,CAS 重试成本就远高于一次排他锁的确定性。


















