Java 并发集合:ConcurrentHashMap 深入解析
Java 并发集合:ConcurrentHashMap 深入解析
1. 概述
ConcurrentHashMap 是 Java 并发包(java.util.concurrent)中的线程安全 Map 实现,支持高效的并发读写操作,适用于高并发环境。
在 Java 7 和 Java 8 版本中,ConcurrentHashMap 采用了不同的底层实现方式,Java 7 采用分段锁(Segment),而 Java 8 进行了优化,使用 CAS(Compare-And-Swap)+ synchronized + Node 结构。
2. 底层数据结构
Java 7 中的 ConcurrentHashMap(Segment 分段锁机制)
- 采用 Segment + HashEntry 结构。
Segment继承ReentrantLock,每个Segment维护多个HashEntry。- 通过分段锁机制提高并发性,每个
Segment维护独立的HashEntry。 - 读操作无锁,写操作使用
Segment级别的锁。

Java 7 中 ConcurrentHashMap 的存储结构如上图,ConcurrnetHashMap 由很多个 Segment 组合,而每一个 Segment 是一个类似于 HashMap 的结构,所以每一个 HashMap 的内部可以进行扩容。但是 Segment 的个数一旦初始化就不能改变,默认 Segment 的个数是 16 个,你也可以认为 ConcurrentHashMap 默认支持最多 16 个线程并发。
Java 8 中的 ConcurrentHashMap(CAS + synchronized 机制)
- 取消了
Segment,采用 数组 + 链表 + 红黑树 结构。 - 使用
Node<K, V>[] table作为基础数据结构,链表冲突超过阈值时转换为红黑树。 - 采用
CAS+synchronized保证线程安全,提高了性能。

Java8 的 ConcurrentHashMap 相对于 Java7 来说变化比较大,不再是之前的 Segment 数组 + HashEntry 数组 + 链表,而是 Node 数组 + 链表 / 红黑树。当冲突链表达到一定长度时,链表会转换成红黑树。
3. 实现原理
1. 读操作(get 方法)
- 读取
table[i]位置的Node,如果key存在,直接返回。 - 在无竞争的情况下,读操作完全无锁。
2. 写操作(put 方法)
- 使用
CAS操作插入数据,避免不必要的锁开销。 - 若
CAS失败,使用synchronized进行加锁。 - 在
Node链表长度超过 8 时,转换为 红黑树 提高查询效率。
3. 扩容机制(rehash 过程)
- 采用 渐进式扩容,避免
HashMap扩容时的阻塞问题。 - 扩容时,采用 转移任务拆分 的方式,由多个线程共同完成。
4. 应用场景
ConcurrentHashMap 适用于高并发场景,如:
- 线程安全的 缓存 组件。
- 统计 业务(如用户请求次数统计)。
- 配置存储(如存储系统配置,避免使用
Hashtable)。
5. 优缺点
优点
✅ 线程安全,支持高并发读写。
✅ 读操作无锁,提高性能。
✅ 写操作部分 CAS 无锁,提高吞吐量。
✅ 采用红黑树优化链表查询效率。
缺点
❌ 不能保证严格的顺序(如 TreeMap)。
❌ 不能存储 null key 或 null value。
❌ 扩容仍然是一个性能瓶颈(尽管已优化)。
6. 替代方案
Collections.synchronizedMap(new HashMap<>()):简单的同步Map,性能较低。ConcurrentSkipListMap:支持 有序 的Map,适用于排序需求场景。ReadWriteLock + HashMap:适用于读多写少的场景。
7. 使用示例
import java.util.concurrent.*;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 插入元素
map.put("apple", 10);
map.put("banana", 20);
// 并发读取
System.out.println("Apple count: " + map.get("apple"));
// 并发修改
map.compute("apple", (key, value) -> (value == null) ? 1 : value + 1);
System.out.println("Updated apple count: " + map.get("apple"));
}
}
8. 总结
- Java 8 的
ConcurrentHashMap采用 CAS + synchronized + 红黑树 提升性能。 - 适用于高并发读写场景,如缓存、计数、共享数据存储。
- 相比
Hashtable和synchronizedMap,具有更高的吞吐量和并发能力。 - 需要避免
null作为键值对,扩容仍需关注性能消耗。
Java7 中 ConcurrentHashMap 使用的分段锁,也就是每一个 Segment 上同时只有一个线程可以操作,每一个 Segment 都是一个类似 HashMap 数组的结构,它可以扩容,它的冲突会转化为链表。但是 Segment 的个数一但初始化就不能改变。
Java8 中的 ConcurrentHashMap 使用的 Synchronized 锁加 CAS 的机制。结构也由 Java7 中的 Segment 数组 + HashEntry 数组 + 链表 进化成了 Node 数组 + 链表 / 红黑树,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表。
有些同学可能对 Synchronized 的性能存在疑问,其实 Synchronized 锁自从引入锁升级策略后,性能不再是问题,有兴趣的同学可以自己了解下 Synchronized 的锁升级。
在多线程环境下,建议优先选择 ConcurrentHashMap 来替代 HashMap 或 Hashtable,以提高性能和并发安全性。
