ConcurrentHashMap1.8 多线程扩容机制

ConcurrentHashMap 1.8 彻底重构了扩容逻辑,抛弃了 1.7 的分段锁 + 全表单线程扩容 ,采用无锁 + CAS + 细粒度锁 实现多线程并行扩容,把扩容性能瓶颈从 O (n) 压到极致,是高并发场景下的核心优化。

我用极简原理 + 核心流程 + 关键细节的方式,清晰拆解这套高效扩容机制:


一、核心设计:为什么 1.8 扩容能做到高效?

  1. 数据结构升级:数组 + 链表 / 红黑树,不再是 Segment 分段锁
  2. 扩容核心思想 :不阻塞写操作 + 多线程分摊迁移任务
  3. 关键标识 :用sizeCtl控制扩容状态、用transferIndex分配迁移桶
  4. 最小迁移单元 :一个桶(链表 / 红黑树),线程只锁当前桶,不锁全表

二、核心扩容变量(看懂这 4 个变量就懂了扩容)

变量 作用
sizeCtl 扩容状态控制:-1 = 正在初始化;<0 = 多线程扩容中(-N 代表 N 个线程);>0 = 下一次扩容阈值
transferIndex 待迁移桶的起始索引,线程通过 CAS 抢占迁移任务,保证不重复、不遗漏
nextTable 扩容后的新数组(容量是原数组 2 倍),扩容完成后替换原 table
fwd 节点(ForwardingNode) 标记当前桶已迁移完成,其他线程访问会被引导去新数组操作

三、多线程并行扩容完整流程(最核心)

1. 触发扩容

  • 元素个数达到阈值sizeCtl
  • 单个桶链表长度≥8 且数组长度 < 64
  • 写线程(put/remove)发现扩容中,自动协助扩容(核心:无专门扩容线程,业务线程兼职扩容)

2. 初始化扩容(第一个触发扩容的线程)

  1. CAS 修改sizeCtl为-1,独占初始化权
  2. 创建nextTable(容量 = 原容量 ×2)
  3. 设置transferIndex=原容量(从最后一个桶往前迁移)
  4. 重置sizeCtl为-(1 + 参与扩容的线程数),标记扩容开始

3. 多线程抢占迁移任务(并行核心)

  1. 所有业务线程(put/get/remove)发现扩容,都会加入协助扩容
  2. 线程通过CAS 修改 transferIndex,批量领取迁移桶(默认一次领取 16 个)
  3. 领取规则:从后往前,每个桶只被一个线程迁移,无竞争
  4. 所有线程分摊迁移任务,容量越大,并行效率越高

4. 单桶迁移逻辑(无锁 + 最小粒度锁)

  1. 线程获取当前桶的头节点,加 synchronized 锁(只锁头节点)
  2. 把当前桶的链表 / 红黑树拆分成 2 份 :
    • 低桶:hash & 原容量 == 0 → 留在新数组原位置
    • 高桶:hash & 原容量 != 0 → 迁移到「原位置 + 原容量」
  3. 迁移完成后,将原桶置为fwd 节点,标记完成
  4. 释放锁,继续迁移下一个桶

5. 扩容期间的读写操作(完全不阻塞)

  • 读操作 :遇到 fwd 节点,直接去nextTable读取,无锁
  • 写操作 :遇到 fwd 节点,协助扩容,再执行写入
  • 只有正在迁移的桶会加锁,其他桶完全并行

6. 扩容完成

  1. 所有桶迁移完毕
  2. 将table指向nextTable
  3. 重置sizeCtl为新阈值(新容量 ×0.75)
  4. 销毁临时变量,扩容结束

四、1.8 多线程扩容的 3 个革命性优化

1. 业务线程兼职扩容,无单独扩容线程

不用等待后台线程扩容,所有线程一起加速,容量越大越快。

2. 最小粒度锁,无全表阻塞

只锁迁移中的桶头节点,99% 的操作无锁并行,并发能力爆炸。

3. 迁移过程无阻塞读写

读操作无缝跳转新数组,写操作协助扩容,彻底解决 1.7 单线程扩容的性能瓶颈。


五、对比 1.7:性能差距有多大?

特性 JDK 1.7 ConcurrentHashMap JDK 1.8 ConcurrentHashMap
扩容方式 单线程全表迁移,全程阻塞 多线程并行迁移,无全表阻塞
锁粒度 Segment 分段锁 桶头节点 synchronized + CAS
扩容性能 O (n),高并发严重卡顿 O (n/M),M = 参与扩容线程数
读写阻塞 扩容期间阻塞所有操作 扩容期间读写几乎无感知
相关推荐
zaizai10076 分钟前
STL深度剖析2
java·c++·算法
染指11108 分钟前
157.Agent-LangGraph-工具(实现ReAct Agent模式)
开发语言·人工智能·python·langchain·agents
程序猿乐锅17 分钟前
【从0-1学习Python | 第九篇】继承
开发语言·后端·python·学习·fastapi
周厉21 分钟前
WebSocket 使用场景与实现方法:Java + Vue3 全栈实战
java·websocket·vue3·websocket双向通讯
wdfk_prog23 分钟前
lwip教程 38:从 `lwipopts.h` 到 Port Contract——lwIP 裁剪、构建选择、OS Port 与 Network Port
java·开发语言
吹什么轩26 分钟前
MySQL 内置函数全景:日期 / 字符串 / 数学 / 其他,一次讲透(附索引杀手排查)
java·数据库·面试
开开心心就好26 分钟前
证件照排版打印工具,免费可自定义尺寸
服务器·开发语言·算法·随机森林·pdf·音视频·散列表
略略略咯咯30 分钟前
12333
java
我是高手高手高高手33 分钟前
宝塔 PHP多版本,切换PHP默认版本
开发语言·php
CodeYp201933 分钟前
第16讲:set 和 map 的使用
开发语言·c++·set·map