Java中CAS操作应通过Atomic类(如AtomicInteger.compareAndSet)间接使用,而非直接调用Unsafe;其返回boolean需配合循环重试,仅保障单变量原子性,跨字段或复杂场景需封装引用或改用带版本号的AtomicStampedReference。

Java 中的 compareAndSwap 方法本身不直接暴露给开发者调用,它是一个底层原子指令(如 x86 的 CMPXCHG),由 JVM 通过 sun.misc.Unsafe 封装为 native 方法(如 compareAndSwapInt)。你不能也不应手动调用 Unsafe 的 CAS 方法来“保证原子性”——原子性由硬件指令天然保障,关键在于**如何正确使用封装后的高层 API**。
必须通过 Atomic 类间接使用 CAS
Java 提供了 java.util.concurrent.atomic 包中的一系列原子类(如 AtomicInteger、AtomicReference),它们内部已安全封装了 CAS 循环逻辑。直接操作 Unsafe 不仅危险、易出错,而且从 JDK 9 起已被限制访问。
-
正确做法:用
AtomicInteger.compareAndSet(expected, newValue)替代手写 CAS 循环 - 错误做法:试图自己读取值 → 判断 → 调用 Unsafe.CAS → 失败再重试,容易漏掉重试逻辑或引发 ABA 问题
-
示例:
counter.compareAndSet(5, 10)表示“只有当前值是 5 时,才把它改成 10”,整个判断+更新一步完成,不可分割
理解 compareAndSet 的返回值并配合循环重试
compareAndSet 返回 boolean,成功才返回 true。若需“一定更新成功”(如自增),必须自行循环,这是乐观锁的标准模式。
- 像
incrementAndGet()这样的方法,内部就是 do-while + CAS 的封装 - 自己实现类似逻辑时,务必确保每次循环都重新读取当前值作为预期值,否则会因过期预期导致无限失败
- 例如实现原子加法:
do { old = get(); } while (!compareAndSet(old, old + delta));
注意 CAS 的适用边界,避免误用
CAS 天然只保障单个变量的原子更新,无法跨字段、跨对象、跨多步操作构成原子性。
- 要同时更新两个整数?不能靠两个
compareAndSet,得用AtomicReference把它们封装进一个不可变对象再 CAS 整体引用 - 涉及数据库、IO 或耗时计算?CAS 自旋会浪费 CPU,此时应退回到
synchronized或显式锁 - 值可能经历 A→B→A 变化?基础 CAS 无法识别,需改用
AtomicStampedReference带版本号校验
不要绕过封装去调用 Unsafe
虽然 Unsafe.compareAndSwapInt(obj, offset, expect, update) 确实存在,但它要求你精确计算字段偏移量、处理内存可见性、管理重试,且在现代 JDK 中需额外打开强约束(--add-opens)才能访问。这违背了 Java 抽象层的设计初衷。
- Atomic 类已为你处理好字段定位、volatile 语义、JIT 优化适配
- 所有标准原子操作(
getAndIncrement、updateAndGet、accumulateAndGet)都基于同一套可靠 CAS 基础 - 除非你在开发 JUC 底层或高性能框架,否则绝不建议触碰 Unsafe
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











