AtomicInteger.compareAndSet是基于CAS指令的无锁原子操作,语义为:仅当当前值等于预期值时才更新为新值并返回true,否则不修改并返回false;它支撑乐观重试机制,典型用法是循环调用get()与compareAndSet()实现线程安全自增等复合操作。

在多线程环境下,AtomicInteger.compareAndSet 是实现无锁(lock-free)并发计数与乐观重试的核心手段。它基于 CPU 的 CAS(Compare-And-Swap)指令,原子地判断当前值是否等于预期值,是则更新为新值,否则失败并返回 false —— 这正是“乐观”策略的基础:先假设能成功,失败后再重试。
理解 compareAndSet 的语义和典型用法
compareAndSet(expected, newValue) 会检查当前整数值是否等于 expected;只有相等才设为 newValue,并返回 true;否则不修改、直接返回 false。它不阻塞线程,也不依赖 synchronized 或 Lock,适合高竞争但更新逻辑简单的场景(如计数器自增)。
常见误用是把 get() 结果直接当 expected 传入,却忽略中间可能已被其他线程修改。正确做法是循环重试,每次重新读取当前值作为比较基准:
- 调用
get()获取当前值current - 计算目标值(如
next = current + 1) - 用
compareAndSet(current, next)尝试更新 - 若返回
false,说明期间有其他线程抢先改了值,回到第一步重试
实现线程安全的自增计数器(乐观重试模式)
下面是一个标准的、基于 CAS 循环的线程安全自增方法:
public int incrementAndGet(AtomicInteger counter) {
int current;
int next;
do {
current = counter.get();
next = current + 1;
} while (!counter.compareAndSet(current, next));
return next;
}
这个循环就是“乐观重试”的体现:不加锁、不挂起,靠反复尝试直到成功。JDK 中 AtomicInteger.incrementAndGet() 内部正是这样实现的(底层还做了少许优化,比如避免重复调用 get())。
注意:该循环在低竞争下极高效;但在极高竞争下可能引发大量 CAS 失败和空转(busy-spin),此时可考虑 LongAdder 等分段累加器来降低热点。
扩展:带业务条件的 CAS 更新(例如仅当值为偶数时加 1)
利用 compareAndSet 的条件性,可以实现更复杂的原子逻辑。比如只在当前计数值为偶数时才执行自增:
public boolean incrementIfEven(AtomicInteger counter) {
int current;
int next;
do {
current = counter.get();
if (current % 2 != 0) {
return false; // 不满足条件,放弃
}
next = current + 1;
} while (!counter.compareAndSet(current, next));
return true;
}
这种写法保持了原子性和线程安全性,且无需外部同步。关键在于所有判断都基于同一时刻的 current 值,更新也仅在条件成立且未被干扰时发生。
注意事项与性能提示
CAS 虽轻量,但仍有隐含成本:
- 每次
compareAndSet都是一次内存屏障操作,影响指令重排和缓存一致性 - 无限循环需确保逻辑最终能成功,避免活锁(例如多个线程反复覆盖彼此的中间状态)
- 不要在循环体内做耗时操作(如 I/O、复杂计算),否则浪费 CPU 且拖慢整体吞吐
- 若更新逻辑复杂或涉及多个变量,CAS 单一变量难以保证整体原子性,应考虑
StampedLock或事务型方案
对于纯计数场景,AtomicInteger + compareAndSet 循环是最简洁、高效的选择;理解其“读–算–比–设”的四步闭环,就能稳稳驾驭乐观并发控制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











