compareAndExchange 总返回当前值(无论是否成功),适用于需旧值做分支判断的场景;weakCompareAndSet 仅返回布尔结果,可能假失败且无happens-before保证,适合可重试的轻量自旋操作。

VarHandle 的 compareAndExchange 和 weakCompareAndSet 都是原子读-改-写操作,但语义、用途和内存行为差异明显。选错可能引发逻辑错误或性能浪费,关键不在“哪个更强”,而在于“哪个更匹配你的场景”。
compareAndExchange:要旧值,也要确定性
它总是返回变量的**当前值**(无论是否成功交换),相当于一次原子的“读取 + 条件写入”。这个返回值就是你操作前的真实快照。
- 适合需要基于旧值做后续判断的逻辑,比如限流器中检查当前请求数、状态机中验证前一状态是否为“INIT”再允许跃迁
- 避免了先 get() 再 compareAndSet() 带来的两次 volatile 读开销,也消除了中间被其他线程修改导致的“值已变但没察觉”的竞态
- 类型安全:返回值与字段声明类型严格一致,不发生 Integer/int 隐式装箱拆箱,没有 Atomic 类里常见的对象引用比较陷阱
- 内存语义可选:有
compareAndExchange(默认)、compareAndExchangeAcquire、compareAndExchangeRelease等变体,按需控制屏障强度
weakCompareAndSet:重试友好,但不保序
它只返回 boolean,表示“是否成功更新”。但它可能“假失败”——值没变,也返回 false;也不提供 happens-before 保证,即不确保对其他线程的可见顺序。
- 底层通常映射为单条 CPU 指令(如 x86 的
cmpxchg),无额外内存屏障,高频自旋场景下比 compareAndSet 更轻量 - 只适用于能容忍假失败、且逻辑天然支持重试的场合,比如无锁栈/队列的 push/pop 循环
- 不能用于跨线程状态同步的关键路径:比如一个线程设了“RUNNING”状态,另一个线程靠 weakCompareAndSet 判断并执行动作,可能因缺少 happens-before 而看不到该状态变更
- 注意命名误导:像
weakCompareAndSetVolatile或weakCompareAndSetAcquire这类带修饰的方法,才提供对应语义的内存效果;裸名的 weakCompareAndSet 是 plain 语义,最弱
怎么选:看你要响应什么
不是性能优先,而是语义对齐。
- 你需要“这次更新前它到底是什么?”→ 用 compareAndExchange
- 你在写一个自旋重试循环,且失败后直接重来即可→ weakCompareAndSet 更高效
- 你要确保状态变更对其他线程立即可见(比如从 STARTING → RUNNING)→ 必须用 compareAndSet 或带 acquire/release 的 compareAndExchange 变体
- 你在替换 Unsafe 字段操作,且希望 JVM 直接生成最优指令→ VarHandle 是标准路径,但必须通过
MethodHandles.lookup()正确构造,不能静态缓存通用 handle
常见误区提醒
别把 weakCompareAndSet 当成“慢一点的 compareAndSet”。它本质是不同契约:一个是“尽力尝试,失败不解释”,一个是“强一致性,失败必有因”。JDK 9+ 中 VarHandle 的这些方法已通过 @HotSpotIntrinsicCandidate 由 JVM 内联为原生指令,性能差距更多来自语义开销,而非实现缺陷。










