首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

JDK1.8 HashMap并发扩容会导致数据丢失吗,大家来看看我的说法对不对?

2026-07-10 1 阅读 约3分钟阅读 贪玩的椅子
分享:
字号:
JDK1.8 HashMap 并发扩容数据丢失问题分析 一、核心结论 JDK 1.8 的 HashMap 仅通过 尾插法 解决了 JDK1.7 并发扩容时 环形链表死循环 问题,但 没有解决并发扩容导致的数据丢失问题 ,HashMap 依旧线程不安全,多线程同时触发扩容依然会出现元素丢失、数据覆盖现象。 二、场景前置条件 HashMap 原始数组 table 容量 = 2 数组下标 1 位置链表: 3 -> 7 -> 5 存在两个并发线程:线程 A、线程 B,同时执行扩容 resize() 方法 JDK1.8 扩容逻辑:按节点 hash 高位区分,原链表拆分为两条子链表,低位链表留在原下标,高位链表迁移至 原下标 + 原容量 位置,采用尾插法迁移节点 三、线程调度时序复现数据丢失过程 步骤 1:两线程同时进入扩容逻辑,线程 A 中途阻塞 线程 A、B 同步读取旧数组,开始遍历下标 1 的链表迁移节点; 线程 A 遍历到节点 e=3 ,预存下一个节点引用 next=7 ,此时 CPU 切换,线程 A 被阻塞挂起。 步骤 2:线程 B 完整执行扩容,完成数组替换 线程 B 不受阻塞,完整遍历整条链表 3->7->5 ,按 hash 拆分迁移: 元素 5:hash 高位为 0,留在新数组下标 1; 元素 3、7:hash 高位为 1,迁移至新数组下标 3,形成链表 3->7->null ; 线程 B 完成全部节点迁移,将扩容后的新数组赋值给 HashMap 全局变量 table ,旧数组引用失效。 此时内存中最新 table 状态: table[1] = 5 table[3] = 3 -> 7 -> null 步骤 3:线程 A 恢复时间片,基于旧引用继续遍历丢数据 线程 A 被唤醒后, 持有的仍是扩容前旧链表的引用 ,继续执行循环: 先处理节点 e=3 ,完成迁移; 读取预存的 next=7 ,赋值给 e ,继续处理节点 7; 读取节点 7 的后继指针 7.next = null , next 赋值为 null; 循环判定 next == null ,直接终止链表遍历。 步骤 4:最终结果 —— 元素 5 永久丢失 线程 A 只遍历了 3、7 两个节点,完全没有读取到节点 5,不会对 5 做任何迁移操作; 线程 A 迁移完成后覆盖数组,最终新数组中不存在元素 5, 数据丢失 。 水
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱