当前位置: 首页 > news >正文

ConcurrentHashMap扩容过程中如何保证更新一致性

在 ConcurrentHashMap(CHM)的扩容过程中,更新一致性(即:不会丢失写入、不会读到中间状态、所有线程看到一致的数据视图)是通过一系列精巧的并发控制机制实现的。下面从 写操作(put/update) 和 读操作(get) 两个维度,深入解析扩容期间如何保证更新一致性。

一、核心原则:迁移完成前,旧表仍有效

CHM 扩容采用 “双表并存 + 原子切换” 策略:

  • 扩容期间,旧表(table)和新表(nextTable)同时存在
  • 只有当一个桶(bucket)迁移完成后,才将旧表该位置替换为 ForwardingNode
  • 在此之前,所有对该桶的读写仍作用于旧表

✅ 这保证了:任何时刻,每个 key 要么在旧表,要么在新表,不会“消失”或“分裂”

二、写操作(put/update)的一致性保障

当线程执行 put(key, value) 时:

情况 1:目标桶尚未迁移(仍是普通 Node)

  • 直接在 旧表 中插入或更新
  • 后续迁移线程会将这个新写入的节点 一起迁移到新表
  • ✅ 不会丢失更新

情况 2:目标桶已迁移(头节点是 ForwardingNode)

  • 当前线程调用 helpTransfer() 协助扩容
  • 扩容完成后,重新执行 put 操作(循环 retry)
  • 此时 key 会直接插入到 新表 的正确位置
  • ✅ 最终写入成功,且只存在于一个地方

🔁 关键:putVal() 方法内部是一个 for(;😉 循环,直到成功插入为止。

for (int i = 0;; ++i) {
if (tab == null || tab.length == 0)
initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, newNode(hash, key, value, null)))
break;
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f); // 协助扩容后,tab 指向新表,继续循环
else {
// 锁住 f,插入或更新
}
}

💡 结论:无论扩容是否发生,put 操作最终都会成功写入唯一一份数据,不会重复、不会丢失。

三、读操作(get)的一致性保障

get(key) 在扩容期间完全无锁,但依然强一致:

  1. 如果桶未迁移
  • 直接从旧表读取 → 正确
  1. 如果桶已迁移(ForwardingNode)
  • 调用 ForwardingNode.find(hash, key)
  • 该方法会 自动跳转到 nextTable 查找
  • ✅ 读到的是最新值
  1. 如果桶正在迁移(但尚未 CAS 成功)
  • 由于迁移是 最后一步才设置 ForwardingNode
  • 此时桶仍是普通链表 → 从旧表读取 → 正确

📌 关键点:

  • ForwardingNode 的设置是 原子的(CAS)
  • Node.next 是 final 的,链表结构不可变
  • table 和 nextTable 字段都是 volatile,保证可见性

因此,get 永远不会看到“部分迁移”的链表,也不会读到 null 或错误值。

四、并发写 + 扩容:如何避免覆盖?

假设有两个线程:

  • 线程 A:正在迁移桶 X
  • 线程 B:同时对桶 X 执行 put

执行顺序分析:

  1. 若线程 B 先执行:

    • 桶 X 还是普通节点 → B 成功写入旧表
    • 线程 A 迁移时会包含 B 写入的新节点 → 一致
  2. 若线程 A 先完成迁移(设置 ForwardingNode):

    • 线程 B 发现 MOVED → 协助扩容 → 重试 put
    • 重试时直接写入新表 → 一致
  3. 若线程 A 正在迁移,但未完成:

    • 桶 X 仍是原链表 → B 写入旧表
    • A 的迁移是 遍历当前完整链表,会包含 B 的写入
    • 最后 CAS 设置 ForwardingNode → 原子切换

✅ 无论如何调度,最终结果都等价于某个顺序执行,满足 线性一致性(Linearizability)

五、特殊场景:红黑树迁移

  • 树节点通过 TreeBin 包装,TreeBin 内部使用 synchronized 保护树结构
  • 迁移时,TreeBin 会加锁遍历整棵树,确保快照一致性
  • 迁移后,新表中的树结构完整,旧表被替换为 ForwardingNode

🔒 树操作本身有同步,迁移过程不会破坏红黑树性质。

六、总结:更新一致性的四大支柱
机制 作用
ForwardingNode 原子标记 明确区分“已迁移”和“未迁移”状态

CAS + volatile 保证状态切换的原子性与可见性

写操作 retry 机制 确保最终写入成功且唯一

不可变链表结构(final next) 避免读到中间状态

🌟 最终效果:

  • 写不丢:所有 put 最终都会持久化
  • 读不错:get 总能返回最新已提交的值
  • 无脏读/幻读:不会看到半迁移的数据
  • 高并发:全程无全局锁,扩容与读写并行

这就是 ConcurrentHashMap 被誉为“并发容器之王”的核心原因之一。

如需进一步了解 computeIfAbsent、merge 等复合操作在扩容中的一致性保证,也可以继续提问!

http://www.jsqmd.com/news/443120/

相关文章:

  • python环境搭建 (七) pytest、pytest-asyncio、pytest-cov 试生态的核心组合 - 教程
  • 北京私募股权基金律师选购攻略 - 工业设备
  • 好用的电源管理芯片供应商推荐有哪些 - 工业品网
  • 说说深圳靠谱的汽车级电源管理芯片厂家有哪些,哪家性价比高 - 工业品网
  • 高校科技成果转化效率低怎么办?
  • 2026年廊坊工程瓷砖批发,这些性价比高的品牌值得选择 - 工业设备
  • 政府科技管理部门如何推动地方科技成果转化?
  • 1×9封装光模块:经典封装,适配多元场景的高可靠之选
  • 构建企业ICT系统带宽保障体系 支撑业务稳定高效运行
  • 科技中介如何提升服务专业性与效率?
  • 网页版编辑器如何处理Word图文及截图粘贴?
  • HTML5如何用原生拖拽API实现文件夹上传并保留完整目录结构?
  • 基于MATLAB的声源定位技术:原理、算法与实现
  • 动态规划 | part13
  • AI编程工具
  • 基于资源池化的ICT基础设施标准化管理与运维实践
  • 产业园区如何提升科技创新服务能力?
  • 分析2026年电源管理芯片品牌商怎么选择 - 工业品网
  • 中央企业如何加快技术成果的商业化转化?
  • WebUploader插件如何扩展HTML5实现文件夹目录结构的断点续传?
  • 盘点全国物流发货服务公司,安徽点对点物流为何口碑出众? - 工业品牌热点
  • 2026年南京金属地垫定制性价比排名,哪家更胜一筹 - mypinpai
  • 构建标准化链路负载体系:实现ICT基础设施高确定性管理
  • 剖析2026年成都离婚律师优质事务所,各机构优势揭秘 - myqiye
  • 2026年成都推荐一下婚姻律师事务所,性价比高的有哪些 - myqiye
  • 大杂烩
  • Lumion 2026教程:5个步骤打造电影级建筑渲染氛围(无需后期处理)
  • 2026年幼猫猫粮产品多维对比:基于核心营养与吸收率的科学评测解析 - 十大品牌推荐
  • 看看2026年性价比高的空气电加热器商家有哪些 - 工业设备
  • 运动医学产品采购:如何筛选口碑与实力兼备的厂家,sports medicine/内窥镜刨削动力代加工,运动医学公司选哪家 - 品牌推荐师