concurrenthashmap 适合搭配线程池,因其读操作无锁、写操作细粒度加锁、扩容支持多线程协作,且提供 computeifabsent 等原子方法,天然适配任务分发与结果归集场景。

ConcurrentHashMap 与线程池协同处理共享任务,核心在于“无锁读 + 细粒度写 + 原子操作”配合“任务分发 + 状态归集”的分工逻辑。它不是靠加锁排队,而是靠结构设计和语义保障让多个线程能安全、高效地往同一个 Map 里写数据或查数据,同时避免竞态和扩容干扰。
为什么 ConcurrentHashMap 适合搭配线程池
普通 HashMap 在多线程写入时可能因扩容形成链表环,导致 CPU 100%;HashTable 虽线程安全,但所有操作串行化,吞吐量低。而 ConcurrentHashMap:
- 读操作完全无锁,多个线程可并发读取,不阻塞
- 写操作只锁定目标桶(Node)级别,不影响其他桶的读写
- 扩容支持多线程协作迁移,不阻塞新写入
- 提供 computeIfAbsent、merge、putIfAbsent 等原子方法,天然适配“检查-计算-写入”类任务
典型协同模式:任务分发 + 结果归集
常见于批量处理、缓存预热、计数统计等场景。例如:100 万条订单按用户 ID 分组统计订单数。
- 用 ExecutorService 提交 20 个并行任务,每个任务处理 5 万条记录
- 所有任务共享一个 ConcurrentHashMap
,key 是 userId,value 是计数器 - 每个线程执行:
map.computeIfAbsent(userId, k -> new AtomicInteger()).incrementAndGet() - 主线程调用
executor.shutdown(); executor.awaitTermination(...)后直接遍历 map 获取结果
关键注意事项与避坑点
协同高效的前提是避开几个典型误用:
- 不要在 computeIfAbsent 的 mappingFunction 里再操作同一 ConcurrentHashMap:比如在里面调用 put 或 remove,可能触发死锁或 IllegalStateException
- 避免把耗时操作塞进原子方法体:如数据库查询、HTTP 请求,应先在外层完成,再用 put/merge 写入结果
- size() 不是实时精确值:它基于 baseCount + CounterCell 分段统计,高并发下可能略滞后;如需强一致性总数,建议用 LongAdder 单独维护
- 遍历时注意弱一致性:迭代器不抛 ConcurrentModificationException,但可能看不到部分刚写入的条目——这属于设计特性,非 bug
一个轻量完整示例
统计一批字符串出现频次:
ConcurrentHashMap<string long> freqMap = new ConcurrentHashMap();
ExecutorService pool = Executors.newFixedThreadPool(8);
List<string> data = generateBigList(); // 比如 100 万条
int chunkSize = 10_000;
for (int i = 0; i {
for (String s : data.subList(from, to)) {
freqMap.merge(s, 1L, Long::sum); // 线程安全累加
}
});
}
pool.shutdown();
pool.awaitTermination(30, TimeUnit.SECONDS);
// 此时 freqMap 已汇总全部结果,可直接使用
</string></string>Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











