ConcurrentHashMap通过分段锁与无锁读实现高性能线程安全:读操作无锁靠volatile,写操作仅锁单个桶,扩容等用CAS,避免HashMap的环形链表和Hashtable的全局锁瓶颈。

因为普通 HashMap 不是线程安全的,而 Hashtable 和 synchronizedMap 又太“重”——ConcurrentHashMap 在保证线程安全的前提下,把锁粒度压到最低,读操作几乎无锁,写操作只锁住单个桶(bin),并发性能远超传统方案。
为什么不能直接用 HashMap
多线程环境下,HashMap 的 put() 可能触发扩容+链表迁移,若多个线程同时操作,极易引发环形链表,导致 get() 死循环、CPU 占满;此外,它不保证可见性与原子性,数据可能丢失或读到脏值。
Hashtable 和 Collections.synchronizedMap 的问题
它们对整个 map 加同一把全局锁:
- 任意线程调用 put、get、size 都要排队等待
- 读写互斥,无法并发读
- 在高并发场景下吞吐量急剧下降,成为瓶颈
ConcurrentHashMap 是怎么做到又快又安全的
JDK 8+ 的核心策略是“分而治之 + 无锁优先”:
立即学习“Java免费学习笔记(深入)”;
- 读操作完全无锁,靠 volatile 修饰的 table 和 Node 字段保证内存可见性
- 写操作(如 put)只对目标桶的头节点加 synchronized 锁,不同桶之间互不影响
- 初始化、扩容、计数等关键步骤大量使用 CAS(如 sizeCtl、nextTable 状态控制)
- 链表过长(≥8)且数组足够大(≥64)时转红黑树,避免哈希碰撞导致的 O(n) 查找
实际使用要注意什么
它强大但有边界,用错反而埋坑:
- 不允许 null 键或 null 值,否则直接抛 NullPointerException
- 迭代器是弱一致性的:遍历时看不到其他线程刚做的修改,也不会抛 ConcurrentModificationException
- 复合操作(如“先查再put”)不是原子的,要用 computeIfAbsent、putIfAbsent、merge 等内置原子方法
- size() 返回的是近似值(基于分段计数+重试),如需精确统计,建议用 mappingCount()
基本上就这些。它不是万能锁,但确实是高并发 Map 场景下的首选——不复杂,但细节容易忽略。










