cas的原子性由cpu硬件指令保障,unsafe通过native方法调用atomic::cmpxchg,最终在x86上执行lock cmpxchg指令;现代cpu优先使用mesi缓存锁而非总线锁,仅在跨缓存行或不可缓存时退化为总线锁。

CAS 的原子性不是 JVM 软件层面“做出来”的,而是由 CPU 硬件指令直接保障的。Unsafe 类是 Java 中暴露这一能力的关键桥梁,它把底层 cmpxchg 指令封装成可调用的 compareAndSwapInt、compareAndSwapLong 等方法,让上层原子类(如 AtomicInteger)得以实现无锁并发。
Unsafe 是如何触发 CAS 的
Unsafe 本身不能被直接 new 或通过 getUnsafe() 获取(除非是 Bootstrap 类加载器加载的类),通常需反射获取实例。它内部的 CAS 方法是 native 实现,最终调用 HotSpot VM 中的 Atomic::cmpxchg 函数,该函数会根据平台生成对应指令:
- 在 x86 架构下,实际执行 cmpxchg 指令,配合 lock 前缀保证多核间操作的全局可见与原子性
- 指令操作目标是内存地址(由对象引用 + 字段偏移量 valueOffset 计算得出)
- 整个过程不涉及操作系统线程调度,也不触发 GC 或 safepoint,极轻量
总线锁并非默认首选,缓存锁才是现代 CPU 的常态
很多人误以为 CAS 一定靠“锁总线”实现,其实不然。现代 CPU(Intel Core 及以后、ARMv8+)优先使用缓存一致性协议(如 MESI)下的缓存锁机制:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 当要 CAS 的变量已缓存在当前 CPU 的 L1/L2 缓存行中,且该缓存行处于 独占(Exclusive)或已修改(Modified)状态,CPU 直接在缓存内完成比较与交换,无需广播总线信号
- 此时仅靠缓存一致性机制(如写回时触发总线嗅探,使其他核失效对应缓存行)即可保证原子性,开销远低于总线锁
- 只有两种情况才会退化为总线锁:一是数据跨缓存行(比如 long 字段横跨两个 64 字节 cache line),二是目标内存无法被缓存(如某些设备寄存器映射区)
总线锁的真实代价与适用边界
总线锁确实存在,但它是硬件兜底机制,不是设计常态:
- 一旦发出 LOCK# 信号,其他 CPU 在该周期内无法访问任何共享内存——相当于暂停了整条内存总线的通信,影响范围远超单个变量
- 在高频率 CAS 场景(如密集计数器更新)中,若频繁触发总线锁,会显著拖慢所有核的内存访问性能,甚至成为瓶颈
- 因此 JDK 和主流 CPU 厂商都极力优化缓存友好性:AtomicInteger 的 value 字段加 @Contended 注解(JDK 8+)、避免 false sharing、字段对齐等,本质上都是为了让 CAS 尽可能落在缓存锁路径上
多处理器环境下 CAS 的一致性保障链条
从 Java 代码到最终内存安全,是一条清晰的硬件-软件协同链:
- Java 层调用 Unsafe.compareAndSwapInt(obj, offset, expect, update)
- VM 层解析 offset 得到实际内存地址 addr,调用 Atomic::cmpxchg(x, addr, e)
- CPU 执行 lock cmpxchg [addr], eax, edx —— lock 前缀激活缓存锁或总线锁
- MESI 协议确保该缓存行变更对其他 CPU 立即可见(通过 write-back + invalidate)
- JMM(Java 内存模型)将该操作视为一个 volatile 写 + volatile 读的组合效果,提供 happens-before 语义










