varhandle 中 compareandset 仅返回是否成功,适合状态切换;compareandexchange 总返回旧值,适合需基于原值计算的原子操作;二者语义不可互换,应按需选用。

Java 11 中,VarHandle 提供了比 Unsafe 更安全、更规范的 CAS 操作支持,其中 compareAndSet 和 compareAndExchange 是两个关键方法。它们语义不同,适用场景也不同:前者关注“是否成功更新”,后者关注“旧值是什么”。选型核心在于——你是否需要知道被替换掉的原始值。
compareAndSet:只关心操作是否成功
compareAndSet 返回 boolean,表示“如果当前值等于预期值,则设为新值,并返回 true;否则返回 false”。它不暴露旧值,适合用在不需要感知原值的同步逻辑中,比如简单的状态切换或计数器递增(配合重试)。
- 语义简洁,开销略小(JVM 可能做更优底层映射)
- 适合无锁算法中“尝试修改 + 失败重试”的典型模式
- 例如:
state.compareAndSet(READY, RUNNING),只在意是否从 READY 成功切到 RUNNING
compareAndExchange:必须拿到旧值才能继续
compareAndExchange 返回 Object 或对应基本类型值(如 int),即“如果匹配则更新,并返回旧值;否则不更新,仍返回当前值”。它保证你总能拿到变量在操作瞬间的实际值,适合需要基于旧值做后续判断或计算的场景。
- 避免重复读取带来的竞态风险(比如先 get 再 cas,中间可能被改)
- 适合实现更复杂的原子逻辑,如原子累加器、带校验的交换、或构建自定义锁状态机
- 例如:实现一个原子 max 更新:
int cur = handle.compareAndExchange(obj, expect, Math.max(expect, update));,靠返回值确认是否真正更新
性能差异不大,但语义不可互换
两者在 x86 上底层都编译为 cmpxchg 指令,实际性能几乎一致。但不能用 compareAndSet 替代 compareAndExchange 来获取旧值——那样需要额外一次 get 调用,破坏原子性;也不能用 compareAndExchange 简单替代 compareAndSet——虽然你可以忽略返回值,但语义上多了一次不必要的值传递,且部分 JVM 实现可能略多一点寄存器操作。
- 优先按语义选:要旧值就用
compareAndExchange,只要结果就用compareAndSet - 注意
compareAndExchange在失败时也返回当前值(非预期值),不是返回expect - 二者都遵循
VarHandle的内存访问模式(如weak/strong),需显式指定(如compareAndSet默认是强一致性,而compareAndSetVolatile等变体可选)
与 AtomicXXX 类的对应关系
AtomicInteger 等类的 compareAndSet 方法行为与 VarHandle.compareAndSet 一致;而其 getAndAccumulate、updateAndGet 等复合操作,底层往往依赖类似 compareAndExchange 的能力来保障原子性。使用 VarHandle 时,若需复现这些高级操作,compareAndExchange 是更直接的构建基础。
-
AtomicInteger.getAndUpdate(x -> x + 1)本质是循环调用compareAndExchange获取当前值并计算新值 - 自己手写无锁栈/队列时,
compareAndExchange更便于构造 head/tail 的原子更新逻辑 -
VarHandle的泛化能力更强(支持任意字段、数组元素、甚至堆外内存),选对方法才能发挥其表达力
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











