cas机制在无锁队列中通过硬件原子指令(如cmpxchg)实现“检查再更新”的不可分割操作,配合volatile保证可见性、自旋重试应对竞争、链式结构或版本号规避aba问题,彻底避免线程阻塞。

CAS 机制在无锁队列(如 ConcurrentLinkedQueue)中不靠锁排队,而是用“检查再更新”的原子动作协调多个线程对 head/tail 指针的修改,从而彻底避免线程阻塞。
靠硬件原子指令完成不可分割的更新
CAS 操作由 CPU 提供的底层指令(如 x86 的 cmpxchg)保证原子性:读取当前值、比对预期值、写入新值——三步合为一个不可中断的动作。队列的入队和出队操作都基于此:
- 入队时,线程先读 tail 节点,构造新节点并设其 next 指向 null,再用 CAS 尝试把 tail.next 从 null 改为新节点地址
- 出队时,线程读 head 和 head.next,再用 CAS 尝试把 head 从旧节点更新为 head.next
- 只要内存位置的值与预期一致,更新就成功;否则立刻返回失败,不挂起线程
用 volatile 保证其他线程及时看到变更
单有 CAS 只能保障一次更新的原子性,但无法确保别的线程马上读到最新值。所以无锁队列中所有关键字段(如 Node.next、head、tail)都声明为 volatile:
- volatile 禁止编译器和 CPU 对这些字段的读写重排序
- 它让每次读取都从主内存加载最新值,每次写入都立即刷回主内存
- 这样,一个线程用 CAS 更新了 tail.next,另一个线程下一次读 tail.next 就能拿到真实状态,不会因缓存旧值而无限重试
失败后自旋重试,而非等待或放弃
CAS 失败不意味着出错,只是说明有竞争。此时线程不做阻塞,而是重新读取当前最新状态,再计算、再尝试:
- 例如入队循环逻辑:
do { oldTail = tail.get(); ... } while (!tail.compareAndSet(oldTail, newTail)); - 这种自旋适用于临界区短、冲突不频繁的场景(如普通业务队列)
- JDK 实现中还加入了轻量级退避(如
Thread.onSpinWait()),缓解高竞争下的 CPU 空转
扩展状态维度防范 ABA 问题
单纯比较指针地址会漏掉“值相同但中间被替换过”的情况。比如 tail 原指向节点 A,线程1暂停;线程2 把 A 出队、插入 B、再出队 B、又把 A 入队——tail 又指向 A,但逻辑结构已不同。
- 标准无锁队列通过节点链式结构天然缓解该问题:每个节点的 next 字段是 volatile 的,且入队必须满足“tail.next == null”才可推进 tail
- 更严谨的实现(如带内存回收的无锁结构)会引入版本号,组成“地址+版本”二元组,用
AtomicStampedReference类型做 CAS,确保即使地址复用,版本不匹配也会失败
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











