AtomicInteger 通过 compareAndSet 支持手动实现带退避的无锁自旋,基础形式为 do-while 循环重试 CAS,但忙等效率低;应引入指数退避策略,结合 Thread.onSpinWait()、Thread.sleep() 或 LockSupport.parkNanos() 控制退避时长,上限建议几微秒至几毫秒。

AtomicInteger 本身不直接提供自定义自旋逻辑或退避策略,但它暴露了底层的 CAS(Compare-And-Swap)原语(compareAndSet),允许你手动实现带退避的无锁重试循环。
用 compareAndSet 实现基础自旋重试
核心思路是:在 while 循环中不断尝试 CAS,失败则继续重试。这是最简形式的无锁自旋:
int expected;
do {
expected = atomic.get();
} while (!atomic.compareAndSet(expected, expected + 1));
但这种“忙等”会持续消耗 CPU,尤其在高竞争场景下效率低、功耗高,因此需要引入退避策略。
加入指数退避(Exponential Backoff)
每次 CAS 失败后,让线程短暂让出 CPU 或暂停,延迟随失败次数增长,降低竞争强度:
立即学习“Java免费学习笔记(深入)”;
- 使用
Thread.onSpinWait()(Java 9+)提示 JVM 当前处于自旋等待,可能触发优化(如轻量级暂停) - 对前几次失败使用
onSpinWait(),后续失败逐步增加Thread.sleep(1)或LockSupport.parkNanos() - 退避上限建议设为几微秒到几毫秒(如最大 100 纳秒 → 1 毫秒),避免退避过长失去无锁优势
示例(简化版):
int spins = 0;
int backoff = 1;
while (true) {
int current = atomic.get();
if (atomic.compareAndSet(current, current + 1)) {
break;
}
if (spins < 10) {
Thread.onSpinWait();
spins++;
} else {
LockSupport.parkNanos(backoff);
backoff = Math.min(backoff << 1, 1_000_000); // 最大 1ms
}
}
结合 ThreadLocal 随机数避免退避同步
多个线程若采用完全相同的退避序列(如都 sleep(1), sleep(2), sleep(4)…),可能在恢复瞬间再次冲突。可引入随机抖动:
- 用
ThreadLocal<Random>生成小范围随机偏移(如 ±25%) - 退避时间 = 基础值 × (0.75 ~ 1.25),打破周期性竞争模式
- 避免使用全局 Random,防止线程间争用种子锁
注意边界与替代方案
自定义退避不是万能的:
- 长时间高竞争下,自旋+退避仍可能劣于阻塞式锁(如
synchronized或ReentrantLock),需实测对比 - 不要在单次操作中做复杂计算后再 CAS —— 若中间有副作用,失败重试会导致重复执行,破坏原子语义
- 若逻辑复杂(如需多变量协同更新),考虑
AtomicReference+ 自定义对象,或StampedLock等更高级结构
不复杂但容易忽略


















