CAS机制通过“检查再更新”的原子操作实现无锁线程安全,依赖CPU指令保障原子性,配合volatile保证可见性与有序性,结合自旋重试应对竞争,并需扩展版本号防范ABA问题。

CAS 机制在无锁数据结构中不靠阻塞排队,而是用“检查再更新”的原子动作来协调多个线程对共享状态的修改,从而实现线程安全。
核心是原子性比较与交换
CAS 操作本身由 CPU 硬件指令保障原子性,它接收三个参数:内存地址 V、期望值 A、新值 B。只有当 V 处当前值等于 A 时,才把 V 更新为 B;否则不做修改,并返回当前实际值。这个“读—比—写”过程不可分割,不会被其他线程打断。
- 比如在无锁栈中,push 操作会读取栈顶指针 current,构造新节点并指向 current,再用 CAS 尝试把栈顶指针从 current 替换为新节点地址
- 若期间有其他线程抢先修改了栈顶,current 就已失效,CAS 失败,当前线程重新读取最新栈顶再重试
- 整个过程没有锁,也没有线程被挂起或调度等待
依赖 volatile + CAS 协同保证可见性与原子性
单纯 CAS 只能保证单次更新的原子性,但无法确保其他线程及时看到变更。因此无锁结构必须配合 volatile 语义:
- 所有被 CAS 修改的字段(如链表头指针、计数器)都声明为 volatile
- voltile 保证该字段的读写操作具有“立即可见性”和“禁止重排序”,使线程能读到最新值,也确保 CAS 前的读操作不会被编译器或 CPU 优化到 CAS 之后
- Java 的 AtomicInteger、AtomicReference 等底层正是这样组合使用的
通过自旋重试应对竞争,而非放弃或阻塞
当 CAS 失败时,线程不会进入阻塞态,而是立即重试——这叫自旋(spin):
- 适用于临界区短、冲突概率低的场景(如计数器累加、简单链表操作)
- 重试逻辑通常封装在循环里,例如:while (!compareAndSet(oldValue, newValue)) { oldValue = get(); }
- 高竞争下可能造成 CPU 空转,所以工业级实现常加入退避策略(如 Thread.onSpinWait() 或指数退让)
规避 ABA 问题需扩展状态维度
标准 CAS 无法识别“值没变但中间被改过”的情况(即 A→B→A),这对某些结构(如带回收的无锁队列)是致命缺陷:
- 典型解法是引入版本号或时间戳,组成“值+版本”二元组,例如 AtomicStampedReference
- 每次修改不仅比对值,还比对版本号;哪怕值回到 A,版本已变,CAS 就失败
- 这相当于把单变量原子操作升级为逻辑上的多字段协同原子性


















