concurrenthashmap 通过 volatile 修饰 table 数组和 node 的 val、next 字段,确保线程间共享状态的修改立即可见,结合不可变字段与 cas 实现无锁读取与安全写入。

ConcurrentHashMap 通过 volatile 修饰关键字段,让线程间对共享状态的修改能被及时“看到”,从而解决多线程下的可见性问题。它不靠锁来同步读写,而是依赖 Java 内存模型(JMM)中 volatile 的语义保障。
table 数组用 volatile 修饰
主哈希表数组 transient volatile Node<k>[] table</k> 被声明为 volatile。这意味着:
- 当一个线程完成扩容并把新数组赋值给
table时,这个引用更新操作对其他线程立即可见; - 其他线程后续读取
table时,不会读到旧的、缓存在本地工作内存中的数组引用; - 这保证了所有线程访问的是同一份最新结构,避免因引用过期导致查不到数据或空指针。
Node 节点内部字段也用 volatile
每个链表/红黑树节点 Node 的核心字段都加了 volatile:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
final int hash;和final K key;是不可变的,天然线程安全; -
volatile V val;:value 值的更新(如put成功后设值)对其他线程立即可见; -
volatile Node<k> next;</k>:链表指针的变更(如插入新节点)也能被其他线程及时感知。
这样,即使 get() 完全无锁,只要顺着 volatile 修饰的 next 指针遍历,就能读到最新构造的链表结构和最新写入的 value。
结合不可变节点与 volatile 实现安全读取
ConcurrentHashMap 在更新 value 时不直接修改原节点,而是创建新节点替换(如 putVal 中生成新 Node 并 CAS 替换)。配合 volatile 的语义:
- 新节点对象一旦被 volatile 字段引用(如
val或next),其字段值(包括对象内部状态)对其他线程可见; - 读线程看到新引用后,顺着 volatile 引用访问其字段,不会读到部分构造或未刷新的中间状态;
- 这就支撑了
get()方法零同步开销,却仍能返回准确结果。
volatile 不是万能的,它只管“看得到”,不管“改得对”
需要特别注意:
- volatile 保证的是单个读/写操作的可见性和有序性,不保证复合操作的原子性(比如先 get 再 put 就不是原子的);
- 所以写操作(
put、remove)仍需 CAS 或 synchronized 配合 volatile 使用——CAS 保证更新动作本身成功,volatile 保证更新后的结果可被别人看见; - 没有 volatile,仅靠锁能保证原子性,但锁释放前的修改未必及时刷回主内存,其他线程可能延迟看到;有了 volatile,连“看到”这一步都交给硬件和 JVM 保障了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










