ConcurrentHashMap 底层具体实现知道吗?实现原理是什么?
                           
天天向上
发布: 2025-07-12 13:15:16

原创
128 人浏览过

ConcurrentHashMap 是 Java 中用于高并发环境下线程安全的哈希表,它的底层实现经历了几个阶段的演进,尤其从 JDK 1.7 到 JDK 1.8,有了较大的结构性变化。


一、核心结论先行

特性Java 7 实现Java 8 实现
数据结构分段锁(Segment + HashEntry)数组 + 链表 + 红黑树(Node[] + TreeNode)
并发控制Segment 级别的锁CAS + synchronized + 自旋
锁粒度默认 16 个 Segment,锁粒度较粗节点级锁,粒度更细
性能中等(锁冲突较多)更高(极低冲突场景几乎无锁)
是否支持 null key/value❌ 不支持❌ 不支持

二、JDK 1.8 中的底层结构(重点)

主结构:Node<K,V>[] table

transient volatile Node<K,V>[] table;
  • 整体类似于 HashMap,但数组元素是 Node 对象(非 Entry)
  • 每个桶(bucket)可能是:
  • 链表(Node -> Node -> …)
  • 红黑树(TreeNode)

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;
}

注意:valnext 是 volatile 的,确保可见性;但结构更新使用的是 CAS(compareAndSwap)


三、并发控制机制详解(JDK 1.8)

1、读操作(get)

  • 采用无锁操作(只读 volatile 字段)
  • 遍历链表或红黑树查找值,完全不加锁 → 高性能
public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e; int n; K k;
    // ... 计算 hash、定位桶位 ...
    // 遍历链表 / 红黑树获取 key 对应的值
}

2、写操作(put)

  • 先通过 CAS(compare-and-swap)尝试插入空桶 → 无需加锁
  • 如果 CAS 失败(冲突),则进入 synchronized 块锁住该桶头节点
  • 插入时:
  • 桶中是链表:插入尾部,如果链表过长 → 转为红黑树
  • 桶中是红黑树:执行红黑树插入算法(如平衡、旋转)
synchronized (f) {
    // 桶头 f 非 null,进行链表/树更新
}

3、扩容操作(resize)

  • HashMap 类似,但使用多线程协作扩容
  • 利用**ForwardingNode 占位符**标记旧桶已转移
  • 使用一个 transferIndex 表示下一个迁移的桶段位置,每个线程搬迁一部分桶位
// 多线程扩容时,每个线程处理一部分桶
if ((f instanceof ForwardingNode)) {
    // 帮助扩容
}

4、TreeNode 结构(红黑树)

static final class TreeNode<K,V> extends Node<K,V> {
    TreeNode<K,V> parent;
    TreeNode<K,V> left, right;
    boolean red;
    // 红黑树插入、查找、旋转...
}
  • 如果某桶链表长度 > 8 且桶数组容量 >= 64,就转换成红黑树
  • 否则仍用链表(节省空间)

四、写时的并发安全如何实现?

操作实现方式
put()先尝试 CAS,失败后锁住桶头(synchronized)
remove()类似 put,先定位,再加锁删除
resize()多线程并发迁移,使用 ForwardingNode 协作
  • 它并不使用全表锁,也不使用传统锁表段(Segment),锁粒度非常小
  • 借助 volatile 保证内存可见性,CAS 保证原子性

五、对比:HashMap vs ConcurrentHashMap

特性HashMapConcurrentHashMap
线程安全性❌ 非线程安全✅ 线程安全
并发读写❌ 可能抛异常✅ 安全
null 键/值支持✅ 支持❌ 都不支持
用于并发场景❌ 不建议✅ 推荐

六、源码入口 & 官方文档


七、面试必会重点总结

问题回答概要
是否支持并发读写?✅ 是,并发读无锁,写锁粒度小
是否支持 null?❌ 不支持 key 或 value 为 null
为什么写操作加锁?保证并发安全,避免链表/树结构竞争
扩容是否会阻塞?❌ 不会,采用多线程协作迁移
底层结构是什么?Node 数组 + 链表 + 红黑树
如何保证线程安全?CAS + volatile + synchronized

更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *