ConcurrentHashMap 的 get 方法不加锁,因其纯读操作不改变共享状态,依靠 volatile 可见性(table、val、next)和 final 不可变性(hash、key)保障一致性,全程无状态变更、节点安全读取、读写路径隔离。

ConcurrentHashMap 的 get 方法不加锁,是因为它本质上是纯读操作,不改变任何共享状态,同时依靠 JVM 内存模型的关键机制——volatile 可见性和final 不可变性——来保障多线程下读取结果的一致与及时。
volatile 保证关键字段的实时可见
ConcurrentHashMap 把所有可能被并发写入、又需被并发读取的字段都声明为 volatile:
- table 数组引用是 volatile 的:扩容后新数组的赋值对所有线程立即可见,get 能立刻切换到新表查找;
- Node.val 和 Node.next 都是 volatile 的:put 写入值或链接下一个节点后,get 遍历链表时不会看到“半初始化”或“缓存旧值”的状态;
- 底层实际调用 Unsafe.getObjectVolatile(),该 native 方法插入内存屏障(load barrier),强制从主存读取,绕过 CPU 缓存,确保读取动作符合 volatile 语义。
读过程全程无状态变更
get 操作只做定位与比对,不触发任何结构修改:
- 计算 hash → 定位桶索引 → 检查首节点是否匹配;
- 若为链表,逐个遍历 next 字段(volatile 读),不改指针、不增删节点;
- 若为红黑树,调用 TreeBin.find(),内部虽有轻量级读锁,但仅用于维持树结构稳定,不阻塞其他桶,也不影响 get 并发;
- 整个流程不修改 table、不更新 size、不触发扩容、不调整链表/树结构——没有竞态条件可被破坏。
节点设计天然支持安全读取
Node 类本身是“读友好型”结构:
- hash 和 key 是 final 的:构造完成后不可变,get 比对 key 时无需担心中途被改写;
- val 是 volatile 的:put 成功写入后,get 一定能读到最新值;
- 即使某个桶正在被 put/remove 锁定,get 仍可并发读取当前已存在的节点——写操作要么插入新节点,要么替换旧节点的 val,两者逻辑上互不干扰。
写操作与读操作天然隔离
ConcurrentHashMap 的同步策略是“写锁极细,读完全不参与”:
- put/remove 等只锁定目标桶的首节点(synchronized(first)),甚至优先尝试 CAS 无锁写入;
- 扩容期间,旧桶中若存在 ForwardingNode,get 遇到后自动转向新表查找,桥接逻辑由 find() 封装,依然不加锁;
- 读与写在数据访问路径上分离,没有共享写状态需要保护,因此无需锁来协调。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











