CAS在Java中并非纯Java实现,而是JVM通过调用CPU原子指令(如x86的lock cmpxchg、ARM的ldrex/strex)配合缓存一致性协议保证原子性;Unsafe类提供native方法作为入口,底层由JVM绑定至平台特定汇编指令,并隐含内存屏障确保可见性。

CAS(Compare-And-Swap)在 Java 中的底层实现,**不是纯 Java 代码写的,而是 JVM 通过调用 CPU 提供的原子指令(如 cmpxchg)配合锁总线或缓存一致性协议(如 MESI)来保证原子性**。Java 层面的 Unsafe.compareAndSwapInt 等方法,最终由 JVM 在运行时绑定到特定平台的本地指令。
Unsafe 类是 CAS 的 Java 入口
java.util.concurrent.atomic 包中所有原子类(如 AtomicInteger)都依赖 Unsafe 类提供的 native 方法:
compareAndSwapInt(Object obj, long offset, int expected, int x)compareAndSwapLong(Object obj, long offset, long expected, long x)compareAndSwapObject(Object obj, long offset, Object expected, Object x)
这些方法声明为 native,实际实现在 JVM 源码中(如 OpenJDK 的 unsafe.cpp),由 JVM 启动时动态绑定到对应 CPU 架构的汇编指令。
JVM 如何把 CAS 映射到底层指令
以 x86_64 平台为例,JVM 在 unsafe.cpp 中调用的是 Atomic::cmpxchg 函数,该函数进一步调用平台相关实现(位于 src/hotspot/os_cpu/linux_x86/atomic_linux_x86.hpp):
立即学习“Java免费学习笔记(深入)”;
- 核心是内联汇编
lock cmpxchg指令:
// 伪代码示意lock cmpxchg %edx, (%rax)—— “锁定缓存行,比较并交换” -
lock前缀确保该指令在多核间原子执行:要么完整执行,要么被阻塞;现代 CPU 通常不锁总线,而是通过缓存锁(cache locking)机制,在 L1 缓存层面保证同一缓存行的互斥访问 - ARM 平台则使用
ldrex/strex指令对实现类似语义
为什么需要 volatile + CAS 配合?
CAS 本身只保证单次操作的原子性,但不解决可见性问题。JVM 对 volatile 字段的读写会插入内存屏障(Memory Barrier):
-
volatile读 → 插入LoadLoad+LoadStore屏障 -
volatile写 → 插入LoadStore+StoreStore屏障 - 而
Unsafe.compareAndSwapXxx内部已隐含了 full barrier(如 x86 上等价于mfence),确保之前所有读写不重排序到 CAS 之后,之后的操作也不提前
所以 AtomicInteger 的 get()、set()、incrementAndGet() 等方法,既是原子的,又能保证跨线程的内存可见性。
HotSpot 源码关键路径参考(OpenJDK 17+)
如果你想看真实实现,可关注以下文件:
-
src/hotspot/share/prims/unsafe.cpp:定义Unsafe_CompareAndSwapInt等 JNI 方法入口 -
src/hotspot/share/runtime/atomic.hpp:声明各平台通用的Atomic::cmpxchg接口 -
src/hotspot/os_cpu/linux_x86/atomic_linux_x86.hpp:x86 下具体汇编实现 -
src/hotspot/cpu/x86/stubGenerator_x86_64.cpp:部分 CAS 相关 stub 生成逻辑
注意:这些代码高度依赖平台和 JVM 版本,不同 JDK(如 ZGC 优化、Shenandoah)可能有细微差异,但核心思想一致——委托给硬件原语。


















