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

jdk1.7 在多线程下扩容可能导致的死循环问题

首先先看看 hashmap 在jdk1.7 下扩容的核心方法

void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; // 遍历旧数组的每一个格子(桶) for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; // 断开旧数组的引用,虽然不是必须的,但有助于GC // 遍历链表,把链表上的节点一个一个摘下来,重新放到新数组里 do { // 【关键点1】先保存下一个节点,防止待会链表断了找不到后面的人 Entry<K,V> next = e.next; // 计算这个节点在新数组中的下标 int i = indexFor(e.hash, newCapacity); // 【关键点2】头插法核心:将当前节点 e 的 next 指向新数组位置原本的头节点 e.next = newTable[i]; // 【关键点3】将新数组该位置的头指针指向 e(e 变成了新的头节点) newTable[i] = e; // 【关键点4】继续处理旧链表的下一个节点 e = next; } while (e != null); } } }

可以看到,jdk1.7 版本下的扩容依靠的是头插法实现元素搬移

头插法 的特点:假设旧数组某个位置的链表是A -> B -> C。 因为代码逻辑是:每次把新来的元素插到最前面(e.next = newTable[i]然后newTable[i] = e)。 所以搬运到新数组后,顺序会倒置,变成C -> B -> A

为什么JDK 1.7用头插法? 设计者当时认为,后插入的数据被访问的概率更高(热点数据),放在头部查得快。但这在多线程下埋下了祸根。

那其实在多线程的情况下就会有问题了,举个例子:

假设有两个线程Thread 1Thread 2同时在扩容。 旧链表结构:A -> B。 线程 1 执行到你注释的那一行挂起了(Paused):

Entry<K,V> next = e.next; // 线程1在这里卡住了! // 此时对于线程1来说: e = A, next = B
接下来可能发生的事情:
  1. 线程 2 抢占 CPU 完成了扩容
    • Thread 2 把AB都搬到了新数组。
    • 注意:因为是头插法,顺序反了
    • 在新内存里,现在的链表结构是:B -> A(B 指向 A,A 指向 null)。
  1. 线程 1 醒来继续执行
    • Thread 1 里的变量还停留在挂起前的状态:e = Anext = B
    • Thread 1 开始干活:
      • 将 A 指向 B
      • 头节点变成 A
      • 此时 A 指向 B,B 指向 A(死循环开始)
  1. 线程 1 接着处理 B,B 头插当头节点,此时 B 指向 A,A 指向 B
  2. 然后接着处理 A,A 头插当头节点,此时 A 指向 B,B 指向 A
  3. 然后循环往复 A,B 轮流当头节点,死循环

如图:


http://www.gsyq.cn/news/115633.html

相关文章:

  • EmotiVoice情感控制技巧:精细调节语音的情绪强度
  • 24、使用SpamAssassin打击垃圾邮件
  • 2025年比较好的85英寸触摸一体机厂家最新TOP排行榜 - 行业平台推荐
  • STM32嵌入式视觉革命:用普通USB摄像头打造智能视觉系统
  • Windows任务栏革命:用Taskbar Groups打造极致高效工作环境
  • 2025值得推荐的低温等离子清洗机供应商TOP5:深度测评指 - mypinpai
  • 融合语音识别与语言理解的技术探索
  • 上下文协议(MCP)Java SDK 指南
  • AMD 780M APU终极优化指南:30%性能提升的完整配置方案
  • 深度解析:基于HoYo.Gacha构建游戏数据挖掘分析平台
  • 智能象棋AI如何提升你的棋艺水平:7天实战进阶指南
  • 19、Kubernetes资源配额、集群容量管理与性能优化
  • 实战指南:使用ACadSharp轻松处理CAD文件,解决工程数据转换难题
  • 网络编程:UDP Socket
  • 验证IP地址(四)
  • ReadCat:终极免费电子书阅读器,重新定义你的数字阅读体验
  • 53、Linux 脚本编程入门指南
  • 手把手教你部署Context7 MCP Server:告别环境配置烦恼
  • JSZip错误处理实战指南:从崩溃到掌控
  • 游戏NPC对话系统新选择:EmotiVoice情感化语音合成实战
  • 34、Linux 帧缓冲设备驱动配置与数据库到文件实用工具指南
  • 48、Linux 系统安全:PAM、文件权限与网络防护
  • EmotiVoice API接口文档解读:快速接入自有系统
  • AutoUnipus智能学习助手:轻松应对U校园课程挑战
  • traceId 传递-controller场景
  • 49、系统安全与性能调优全攻略
  • qt串口助手
  • GPT-5.2 升级红利吃透指南:从版本碾压到3步极速落地
  • Blender建筑生成插件building_tools:3步创建专业建筑模型
  • EmotiVoice在直播行业的创新应用设想