cas是乐观锁在java中最典型的实现,它基于“先试再判”思想,不加锁而通过compareandset校验预期值,失败后自旋重试,适用于读多写少的高并发场景。

CAS 是乐观锁在 Java 中最典型的落地实现,它不靠阻塞等待,而是靠“先试再判”来应对并发——认为多数时候没人改,只在真正更新时校验是否被动过。这种思路天然适配读多写少、冲突概率低的高并发场景,比如计数器、状态标记、配置缓存更新等。
CAS 如何体现乐观锁思想
乐观锁的核心假设是:操作期间数据大概率未被修改。CAS 完全遵循这一逻辑:
- 不提前加锁,线程直接读取当前值(比如 AtomicInteger 的 get()),拿到“旧值”作为预期值
- 执行业务计算(如 +1),得到新值
- 调用 compareAndSet(旧值, 新值) —— 仅当内存中值仍等于旧值时才成功替换,否则失败
- 失败后不挂起,而是重新读、再算、再试(自旋),直到成功或主动放弃
整个过程没有线程阻塞,也没有操作系统级锁参与,完全在用户态完成,这正是“乐观”二字的实质:信自己能成,不成就重来。
CAS 在无锁高并发中的典型应用
Java 并发包大量依赖 CAS 构建无锁结构,关键场景包括:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 原子计数器:AtomicInteger.incrementAndGet() 底层就是 CAS 自旋,适用于 PV 统计、限流令牌桶计数等高频读+低频写的场景
- ConcurrentHashMap 写操作:JDK8+ 中 put、compute 等方法对链表头/红黑树根节点的更新均使用 CAS,避免全局锁,提升并发吞吐
- AQS 同步状态管理:ReentrantLock 加锁本质是 CAS 修改 state 字段;CountDownLatch 的 countDown() 也是 CAS 减值
- 无锁栈/队列:如 ConcurrentLinkedQueue 的入队出队,全部基于 CAS 操作 Node 的 next 和 item 字段
CAS 带来的核心优势
相比 synchronized 或 ReentrantLock 这类悲观锁,CAS 在合适场景下带来三方面实质性收益:
- 零上下文切换开销:线程始终运行,不进入阻塞态,避免内核态与用户态切换,尤其在多核 CPU 上吞吐更高
- 无死锁风险:不持有锁资源,不存在循环等待条件,从根本上规避死锁
- 细粒度控制能力:可针对单个字段甚至单个 bit 位做原子操作(如 AtomicIntegerFieldUpdater),比粗粒度锁更灵活
当然,这些优势成立的前提是冲突率不高。若多个线程频繁争抢同一变量,自旋会浪费 CPU,此时悲观锁反而更稳。
不可忽视的局限与应对
CAS 不是银弹,实际使用需直面三个关键限制:
- ABA 问题:值从 A→B→A,CAS 误判为未变。解决方案是 AtomicStampedReference,用版本号区分“表面相同但过程不同”
- 只能保障单变量原子性:无法像 synchronized 那样包裹多行逻辑。涉及多个字段更新时,需引入锁或设计成不可变对象+CAS 整体引用
- 自旋耗 CPU:长时间失败可能拖慢系统。可通过 Thread.yield()、LockSupport.parkNanos() 加入退避,或结合超时机制降级为锁
本质上,CAS 不是替代锁的工具,而是提供了一种轻量、非阻塞的协作方式——它把“冲突处理”的责任交还给上层逻辑,让开发者根据业务特征做取舍。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










