一、数据结构 (Data Structure)
Java 8 的 ConcurrentHashMap 摒弃了 Java 7 中的 Segment 分段锁 机制,采用了与 HashMap 1.8 类似的 数组 + 链表 + 红黑树 的结构,但在并发控制上做了特殊设计。
1. 核心结构图
ConcurrentHashMap ├── Node[] table (volatile) // 哈希桶数组
│ ├── Node (链表节点)
│ ├── TreeBin (红黑树节点包装)
│ └── ForwardingNode (扩容迁移标志)
├── Node[] nextTable // 扩容时的新数组
├── LongAdder baseCount // 基础计数
└── CounterCell[] counterCells // 并发计数单元格 (解决热点竞争)
2. 关键节点类型
- Node: 存储键值对的基本节点,包含
key,val,hash,next。 - TreeBin: 当链表长度超过阈值(默认 8)且数组长度超过 64 时,链表转为红黑树。
TreeBin是红黑树的根节点包装,不直接存储数据,而是维护树的平衡。 - ForwardingNode: 专门用于扩容。当某个桶正在被迁移时,该位置会放置一个
ForwardingNode,其hash值为MOVED (-1),指向新的数组nextTable,引导后续请求去新数组操作。
3. 并发控制核心
- CAS (Compare-And-Swap): 用于无锁插入节点(如数组初始化、空桶插入)。
- synchronized: 当发生哈希冲突(链表或树操作)时,只锁定当前桶的 头节点 (Head Node)。粒度更细,性能优于 Java 7 的 Segment 锁。
- volatile:
table数组和Node的val、next指针都是 volatile 的,保证可见性。
二、put() 方法执行流程
putVal 是 put 的核心实现,流程较为复杂,主要步骤如下:
1. 参数检查与 Hash 计算
- 检查 key 和 value 是否为 null(CHM 不允许 null)。
- 计算 key 的 hash 值(使用扰动函数,高位参与运算,减少冲突)。
2. 数组初始化 (initTable)
- 如果
table为 null 或长度为 0,调用initTable()。 - 使用 CAS 竞争初始化权。如果当前线程发现
sizeCtl < 0,说明其他线程正在初始化,当前线程让出 CPU () 自旋等待。


