CAS失败后必须重试,因为其仅在预期值与当前值相等时更新成功,否则返回false,表明变量已被其他线程修改;不重试将导致更新丢失、业务逻辑中断。

CAS(Compare-And-Swap)是 Java 中实现乐观锁的核心机制,它不加锁,而是通过原子比较并更新来保证线程安全。但 CAS 本身失败时不阻塞,需要上层配合重试逻辑,才能在并发抢占冲突时最终达成一致状态。
为什么 CAS 失败后必须重试?
CAS 操作只有在当前值等于预期值时才更新成功;否则返回 false,说明期间有其他线程修改了该变量。这本身不是错误,而是乐观锁的正常反馈——它假设冲突少,但必须主动应对冲突。不重试就等于放弃更新,业务逻辑可能无法推进。
典型重试模式:循环 CAS(Spin Loop)
最常用方式是用 while 循环不断读取最新值、重算预期值、再尝试 CAS,直到成功为止:
示例:原子计数器自增
立即学习“Java免费学习笔记(深入)”;
AtomicInteger counter = new AtomicInteger(0);
int oldValue, newValue;
do {
oldValue = counter.get();
newValue = oldValue + 1;
} while (!counter.compareAndSet(oldValue, newValue));
说明:
- 每次循环都重新读取当前值,避免基于过期快照计算
- compareAndSet 是 CAS 的封装,返回 boolean 表示是否成功
- 没有锁开销,但高争用下可能空转耗 CPU(需结合业务权衡)
进阶:带条件/带限制的重试
纯无限循环不适合所有场景,可加入控制策略提升健壮性:
- 最大重试次数:防止极端情况下的无限等待,失败后抛异常或降级处理
- 退避策略:如 Thread.yield() 或小间隔 sleep,缓解 CPU 竞争
- 业务语义重试:比如更新余额前需校验账户状态,失败后要重新加载完整业务对象再试
例如限制 100 次重试:
for (int i = 0; i < 100; i++) {
int cur = ref.get();
int next = computeNext(cur); // 依业务重算
if (ref.compareAndSet(cur, next)) {
break;
}
if (i == 99) throw new IllegalStateException("CAS failed after 100 retries");
}
与 ABA 问题的协同处理
CAS 无法识别 A→B→A 的变化(即值变回原样但语义已不同),可能导致逻辑错误。Java 提供 AtomicStampedReference 为引用附带版本戳,重试时不仅要比对值,还要校验 stamp:
- 每次 CAS 成功后,stamp 自增
- 重试循环中同时读取 reference 和 stamp,确保二者均未被中途篡改
- 适用于栈顶弹出、链表节点替换等对中间状态敏感的场景


















