volatile在并发容器中精准用于保障状态可见性与指令顺序:修饰sizectl和nexttable确保状态标志与数组引用的即时可见,修饰concurrentlinkedqueue的next字段支持无锁遍历,但不保证复合操作原子性。

volatile 在并发容器底层源码中不是“随处可见”,而是被**精准用于保障状态可见性与指令顺序的关键位置**——它不参与锁竞争、不保证复合操作原子性,但为高性能无锁或半锁结构提供了轻量级同步基础。
volatile 修饰状态标志位:控制容器生命周期
在 ConcurrentHashMap(JDK 8+)中,sizeCtl 字段被声明为 volatile:
private volatile int sizeCtl;
该变量承担多重角色:初始化控制(-1)、扩容触发阈值(正数)、扩容中标识(-N)。多个线程需实时感知其变化,例如:
- 一个线程将
sizeCtl设为 -1 表示正在初始化,其他线程读到该值就不再重复初始化; - 扩容线程将其设为负数(如 -2),协助线程通过 volatile 读立即获知扩容进行中,避免误操作。
这里 volatile 保证了“状态变更的即时通知”,是无锁协作的前提。
volatile 修饰数组引用:确保新结构对所有线程可见
在 ConcurrentHashMap 的扩容逻辑中,nextTable 字段也是 volatile:
private transient volatile Node
当扩容完成,主线程将构建好的新桶数组赋值给 nextTable,其他工作线程需立刻看到这个新数组并迁移数据。若无 volatile:
- 写线程可能只更新了本地缓存中的引用,未及时刷回主内存;
- 读线程可能长期持有旧的 null 或过期数组引用,导致数据丢失或死循环。
volatile 写保证了引用更新的“发布效应”(happens-before),使整个新数组结构对所有线程安全可见。
volatile 修饰节点字段:支持无锁链表遍历
在 ConcurrentLinkedQueue 中,节点 Node 的 next 字段被声明为 volatile:
volatile Node
这是实现无锁队列的核心设计之一。入队时,CAS 设置 tail.next;出队时,通过 volatile 读取 next 来判断是否到达链表尾部或跳过已删除节点。volatile 保证了:
- 一个线程写入
next后,其他线程能立即看到该链接关系; - 禁止编译器/CPU 将
next的读操作重排到节点构造完成之前(防止读到未初始化的 next)。
这种细粒度的 volatile 使用,支撑了 lock-free 算法的正确性。
不适用 volatile 的地方:原子性操作交给 CAS 或锁
需要强调的是,并发容器中涉及计数、修改、删除等复合逻辑的位置,从不依赖 volatile 保证正确性:
-
ConcurrentHashMap的put操作使用 synchronized 锁住单个桶,或用 CAS + 自旋; -
AtomicIntegerArray的incrementAndGet()依赖 Unsafe 的 CAS,而非 volatile++(后者非原子); -
CopyOnWriteArrayList的写操作全程加锁,volatile 仅用于修饰内部数组引用(array字段),确保读线程总能看到最新快照。
volatile 是“信号灯”和“门帘”,不是“执行者”。它让关键状态和引用变更可被快速感知,而真正的协调与保护,由更重的机制承担。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











