ConcurrentHashMap 的 get 操作无锁,依靠 volatile 读和不可变性实现线程安全:table 数组、Node 的 val 和 next 字段为 volatile,hash 和 key 为 final;扩容时通过 ForwardingNode 透明跳转新表;提供近似快照的弱一致性而非强一致性。

ConcurrentHashMap 的 get 操作是无锁的,核心在于它利用了 volatile 读 和 不可变性设计,配合数组 + 链表/红黑树的分段结构,避免了读操作加锁的开销。
volatile 保证可见性,无需加锁读取
ConcurrentHashMap 的底层 Node 数组(table)被声明为 volatile:
这意味着每次读取 table 或其元素(如 tab[i])时,JVM 会强制从主内存加载最新值,确保线程能看到其他线程对数组引用或节点的最新写入。即使没有 synchronized 或 Lock,也能安全读取当前快照。
Node 字段用 volatile 或 final 保障字段可见性
每个链表或红黑树节点(Node)的关键字段都具备内存可见性保障:
立即学习“Java免费学习笔记(深入)”;
-
val字段是volatile—— 读取 value 时能立即看到最新写入; -
next字段也是volatile—— 遍历链表时能正确看到后续节点; -
hash和key是final—— 构造后不可变,天然线程安全。
这些设计使得 get(K key) 只需按 hash 定位桶、遍历链表/树,全程不修改任何共享状态,也无需同步。
扩容期间仍可安全 get:使用 ForwardingNode 协同
当发生扩容(transfer)时,原数组某些桶会被置为 ForwardingNode,它本身是一个特殊节点,持有新表引用。此时 get 遇到该节点会自动转向新表查找:
- 不会阻塞,也不依赖锁;
- 利用
volatile读取ForwardingNode.nextTable,确保看到已发布的扩容进度; - 整个过程对读操作透明,用户无感知。
不依赖锁,但不等于“强一致性”
需要明确:get 是无锁且安全的,但它读到的可能是“某个时间点的近似快照”:
- 无法保证与其他线程的
put/remove绝对实时同步; - 例如,刚插入一个键值对,
get可能因 CPU 缓存、重排序等原因暂时读不到(但很快可见,通常在几纳秒到微秒级); - 这是无锁读的合理取舍——用弱一致性换高性能。
不复杂但容易忽略。


















