volatile 与 cas 是 doug lea juc 中分工协作的一体化机制:volatile 保障可见性与有序性,cas 提供原子性尝试能力,二者结合支撑无锁数据结构。

volatile 和 CAS 在 Doug Lea 的 JUC 实现中不是并列的“两个技术”,而是分工明确、彼此补位的一体化协作机制:volatile 提供**可见性与有序性保障**,CAS 提供**原子性尝试能力**;二者缺一不可,共同支撑起无锁(lock-free)数据结构的精巧运转。
volatile 是状态的“广播站”
在 ConcurrentLinkedQueue、LinkedTransferQueue、AQS 等源码中,volatile 字段几乎总是出现在关键指针上——比如 head/tail 节点引用、AQS 中的 state、AtomicInteger 的 value。这些字段本身不执行原子更新,但承担着“让所有线程立刻看到最新值”的核心职责。
Lea 不用 volatile 去做“原子自增”,而是用它来发布结构变化的信号:
- 当 tail 被 volatile 修饰,一个线程成功 CAS 更新 tail 后,其他线程下一次读 tail 就能立即看到新地址,无需加锁等待;
- AQS 中 volatile int state 的读写,配合 acquire/release 语义,天然构成 acquire-release 内存屏障,使临界区前后的内存操作不会被重排序;
- volatile 还隐式防止编译器和 CPU 对其前后指令做有害重排——这是 CAS 能“安全重试”的前提。
CAS 是动作的“试探员”
CAS 从不单独存在,它永远作用于一个 volatile 变量之上。以 AQS 的 tryAcquire 为例:
- 先 volatile 读取 state —— 确保拿到的是最新值(不是缓存旧值);
- 再用该值作为预期值 A,调用 Unsafe.compareAndSwapInt —— 尝试原子更新;
- 失败则重试:因为 volatile 保证了下次读仍是最新值,所以重试逻辑才不会陷入“基于过期快照的无效竞争”。
这种“volatile 读 → CAS 尝试 → 失败再 volatile 读”的循环,正是无锁算法的呼吸节奏。Lea 的代码里极少见到 while(true) { ... } 无条件自旋,而是精准控制在 volatile 变量发生变化的边界上重试。
组合之美体现在“分离关注点”
Doug Lea 的设计哲学是把并发问题拆解为正交责任:
- 谁负责“让别人看见我改了”?→ volatile(JVM 层语义,硬件级缓存失效);
- 谁负责“确保我改的时候没人同时改”?→ CAS(CPU 硬件指令,原子比较+写入);
- 谁负责“改失败了怎么办”?→ 算法逻辑本身(比如自旋、退避、转锁、或切换路径)。
你看 LinkedTransferQueue 的 qnode 的 isData、next 等字段全是 volatile,而入队出队的核心逻辑全部围绕 Unsafe CAS 展开——没有 synchronized,没有 monitor,只有指针跳转与状态跃迁,像一台精密钟表里齿轮咬合般严丝合缝。
注意那些“刻意为之”的细节
真正体现大师功力的,往往是容易被忽略的工程选择:
- 伪共享规避:LinkedTransferQueue 中,head/tail 指针之间插入 7 个 long 字段(@Contended 或手动填充),确保它们落在不同缓存行——这是为了让 volatile 写 tail 不导致 head 缓存行无效,避免无谓性能损耗;
- volatile 读 + CAS 写 = 最小开销同步原语:比 synchronized 轻量,比纯 volatile 安全,比纯 CAS 可靠;
- 不依赖锁,但保留降级能力:AQS 在 CAS 失败多次后会挂起线程,但初始路径仍是无锁的——优雅退化,而非非此即彼。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











