ConcurrentHashMap 是 Java 并发包中提供的线程安全且高效的哈希表实现,适用于多线程环境。其设计结合了分段锁、CAS(Compare-And-Swap)操作和细粒度同步策略,以实现高并发性能。本文将从数据结构、并发机制、关键操作等方面展开详解。
一、数据结构
- 
基础结构
与 HashMap 类似,底层采用 Node 数组 + 链表/红黑树:
- Node 数组(table):存储键值对,每个数组元素称为一个"桶"。
 - 链表:哈希冲突时,同一桶内的元素形成链表。
 - 红黑树:当链表长度 ≥ 8 且数组长度 ≥ 64 时,链表转为红黑树,优化查询效率(退化为链表的阈值为 6)。
 
 - 
节点类型
- 普通节点(Node) :存储键值对,
hash值为正数。 - 转发节点(ForwardingNode) :扩容时标记已迁移的桶,
hash值为MOVED(-1)。 - 树节点(TreeBin) :红黑树的根节点,
hash值为TREEBIN(-2)。 
 - 普通节点(Node) :存储键值对,
 
二、并发控制机制
- 
CAS 无锁化操作
- 初始化数组:通过 CAS 确保只有一个线程初始化数组。
 - 插入头节点:若桶为空,使用 CAS 插入新节点,避免锁竞争。
 
 - 
细粒度锁(synchronized)
- 锁定桶的头节点 :当发生哈希冲突(链表或树已存在),对头节点加 
synchronized锁,保证同一桶的操作线程安全。 - 锁粒度小:不同桶的操作可并发执行。
 
 - 锁定桶的头节点 :当发生哈希冲突(链表或树已存在),对头节点加 
 - 
多线程协作扩容
- 
触发条件 :元素数量超过
容量 × 负载因子(默认 0.75)。 - 
扩容过程:
- 创建新数组 
nextTable(容量翻倍)。 - 分配迁移任务:线程通过 
transferIndex领取迁移区间(一个或多个桶)。 - 迁移数据:将旧数组中的元素复制到新数组,遇到转发节点则跳过。
 - 完成迁移:所有线程完成后,替换旧数组为 
nextTable。 
 - 创建新数组 
 
 - 
 
三、关键操作流程
- 
put 操作
- 
步骤:
- 
计算键的哈希值(
spread方法处理哈希码)。 - 
若数组未初始化,则初始化(CAS 控制)。
 - 
定位到桶,若桶为空则 CAS 插入新节点。
 - 
若桶非空,锁住头节点:
- 链表:遍历链表,更新或插入节点。
 - 红黑树:通过 
TreeBin插入节点。 
 - 
检查是否需要扩容。
 
 - 
 - 
并发安全 :CAS 插入头节点 +
synchronized锁。 
 - 
 - 
get 操作
- 无锁读取 :直接访问数组元素,依赖 
volatile修饰的next指针保证可见性。 - 处理转发节点 :若遇到 
ForwardingNode,则到新数组中查找。 
 - 无锁读取 :直接访问数组元素,依赖 
 - 
size 操作
- 统计方式 :基于 
baseCount和CounterCell[](类似 LongAdder 的分段计数)。 - 最终值 :
size = baseCount + ∑CounterCell[i].value。 
 - 统计方式 :基于 
 
四、特性与优化
- 
线程安全
- 通过 CAS 和 
synchronized实现无锁化和细粒度锁,避免全局锁竞争。 
 - 通过 CAS 和 
 - 
高效扩容
- 多线程协作:多个线程可同时参与数据迁移,加快扩容速度。
 - 迁移标记 :转发节点(
ForwardingNode)指示其他线程跳过已处理的桶。 
 - 
迭代器弱一致性
- 不抛出异常:迭代过程中允许其他线程修改 Map。
 - 数据可能过期:迭代器创建时捕获当前数组状态,不反映后续修改。
 
 - 
哈希算法优化
- 
spread方法:将哈希码高 16 位与低 16 位异或,减少冲突概率。arduinojava static final int spread(int h) { return (h ^ (h >>> 16)) & HASH_BITS; } 
 - 
 
五、与 Hashtable 的对比
| 特性 | ConcurrentHashMap | Hashtable | 
|---|---|---|
| 锁机制 | 分段锁(桶级别) + CAS | 全表锁(方法级 synchronized) | 
| 并发性能 | 高(多线程可操作不同桶) | 低(全局锁导致串行化) | 
| Null 支持 | 不允许 null 键或值 | 不允许 null 键或值 | 
| 迭代器 | 弱一致性(不抛异常) | 强一致性(可能抛异常) | 
| 扩容策略 | 多线程协作扩容 | 单线程扩容 | 
六、使用场景
- 高并发读写:如缓存系统、实时数据处理。
 - 替代 Hashtable:需更高并发性能的场景。
 - 线程安全 Map:多线程环境下需保证数据一致性。
 
七、注意事项
- 哈希冲突设计 :确保键的 
hashCode()和equals()正确实现。 - 容量规划:合理设置初始容量和负载因子,减少扩容次数。
 - 避免死锁:尽管锁粒度小,仍需避免跨桶的复合操作导致死锁。
 
通过上述机制,ConcurrentHashMap 在多线程环境中实现了高性能的线程安全操作,是 Java 并发编程中不可或缺的核心类之一。