ConcurrentHashMap是Java并发编程中常用的线程安全哈希表实现,它的get方法在不加锁的情况下依然能保证读取到正确的数据,这一特性让很多开发者好奇其背后的实现逻辑,核心原因可以从volatile读特性和Node结构的设计两方面来分析。

ConcurrentHashMap的基础结构
JDK 1.8之后的ConcurrentHashMap放弃了分段锁的设计,采用数组加链表加红黑树的结构,核心的数组成员是Node<K,V>[] table,所有对table的修改操作会通过CAS或者synchronized保证线程安全,而读取操作则通过特殊的设计避免加锁。
Node结构的设计细节
ConcurrentHashMap中的核心节点是Node,其结构定义保证了节点内的关键字段具备可见性,我们来看它的核心源码:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
// 省略构造方法和其他方法
}
从源码可以看到两个关键字段被volatile修饰:
val:存储节点的具体值,volatile修饰保证修改后对其他线程立即可见next:指向下一个节点的指针,volatile修饰保证链表结构的修改对其他线程立即可见
同时hash和key字段被final修饰,一旦节点创建完成就不会被修改,避免了读取时的不一致问题。
volatile读的特性
volatile关键字修饰的变量,在读取时会触发volatile读语义:
- volatile读不会被本地内存缓存,每次都会从主内存中读取最新值
- volatile读之前的普通读操作不会被重排序到volatile读之后,保证读取顺序的正确性
这意味着当其他线程修改了Node的val或者next字段后,当前线程调用get方法读取这些字段时,能立刻获取到最新的值,不会读到过期的缓存数据。
get方法不加锁的原理分析
我们来看get方法的核心实现逻辑:
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
// 计算key的哈希值
int h = spread(key.hashCode());
// 如果table不为空,且对应索引位置的节点不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
// 如果第一个节点的hash和key匹配,直接返回
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
// 如果是红黑树节点,调用红黑树的查找方法
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
// 遍历链表查找匹配的节点
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
get方法的执行流程完全基于volatile读特性:
- 首先读取table数组的引用,table数组本身被volatile修饰,保证能读到最新的数组引用
- 通过
tabAt方法读取数组对应位置的Node,该方法内部通过volatile读获取节点,保证能读到最新的节点引用 - 读取节点的val和next字段时,由于这两个字段是volatile修饰的,每次读取都是主内存的最新值
整个过程中,get方法只进行读取操作,不会修改任何共享变量,而所有可能被修改的共享变量(table引用、Node的val和next)都具备volatile的可见性保证,因此不需要加锁就能保证读取到正确的数据。
特殊情况说明
需要注意的是,get方法只能保证读到已经完成的put操作的结果,如果某个线程正在执行put操作但还没完成,get方法可能读不到这次修改的中间状态,这并不违反ConcurrentHashMap的线程安全语义,因为ConcurrentHashMap保证的是最终一致性,而不是强一致性。
ConcurrentHashMap的设计思想是读写分离,写操作通过加锁保证互斥,读操作通过volatile特性保证可见性,从而在保证线程安全的同时最大化读取性能。
ConcurrentHashMapvolatile读Node结构get方法线程安全修改时间:2026-06-10 06:09:25