HashMap 在多线程环境下可能引发哪些问题?如何解决?


HashMap 在多线程环境下可能引发哪些问题?

答案:
在多线程同时操作 HashMap 时,可能引发 死循环数据丢失脏数据读取 等问题。
根本原因:
HashMap 的设计是非线程安全的,多线程并发修改其结构(如扩容、插入、删除)会导致内部链表或红黑树结构损坏。


具体问题分析:

1. 死循环(JDK 1.7 及之前版本的经典问题)
  • 触发场景: 多个线程同时触发扩容(resize)。
  • 原因:
    JDK 1.7 的扩容采用“头插法”迁移链表,多线程并发操作可能导致链表形成环状结构(循环链表)。
    举例:
    想象两个搬运工(线程)同时把书从一个旧书架搬到新书架,但搬运时不小心把书的顺序弄反了,结果某些书被循环引用,再也找不到正确的顺序了。
2. 数据丢失
  • 触发场景: 多个线程同时插入新数据。
  • 原因:
    两个线程同时计算哈希并定位到同一个桶(数组位置),后插入的线程可能覆盖前一个线程写入的值。
    举例:
    两个人(线程)同时往同一个抽屉里放文件,后放的人直接把自己的文件盖在别人的文件上,导致别人的文件丢失。
3. 脏数据读取
  • 触发场景: 一个线程正在扩容,另一个线程尝试读取数据。
  • 原因:
    扩容过程中链表可能被临时拆分成两部分,此时读取的数据可能不完整或为旧数据。
    类比:
    搬家时(扩容),你一边搬箱子一边查字典(读数据),可能查到的词条是已经搬走的旧箱子里的内容。

如何解决?

方案 1:使用线程安全的替代类
  • 推荐方法: ConcurrentHashMap
    原理:

    • JDK 1.7 采用分段锁(每个段独立加锁,提高并发度)。
    • JDK 1.8 改为基于 CASsynchronized 锁单个桶(更细粒度)。
      举例:
      把仓库分成多个小房间(分段),每个房间有独立的锁,搬运工可以同时操作不同房间,互不干扰。
  • 其他方法(不推荐):

    • Hashtable:全表锁,性能差(类似整个仓库只有一把锁,所有人排队操作)。
    • Collections.synchronizedMap(new HashMap()):包装类,同样全表锁。
方案 2:手动加锁(仅限特殊场景)
  • 方法: 使用 synchronizedReentrantLock 包裹所有 HashMap 操作。
    缺点: 完全串行化,性能极低,不如直接使用 ConcurrentHashMap

举个栗子 🌰

问题复现(死循环):

// JDK 1.7 环境下运行以下代码
HashMap<Integer, Integer> map = new HashMap<>(2); // 容量2,阈值1.5
// 线程1和线程2同时执行 put 操作触发扩容
map.put(5, 5);  // 哈希冲突可能导致链表成环

此时调用 map.get(5) 可能陷入死循环(CPU 100%)。

解决方案:
直接替换为 ConcurrentHashMap

ConcurrentHashMap<Integer, Integer> safeMap = new ConcurrentHashMap<>();
// 多线程操作安全

总结

问题触发场景解决方案
死循环多线程并发扩容(JDK1.7)使用 ConcurrentHashMap
数据丢失多线程同时插入同一位置使用线程安全的容器
脏数据读取扩容与读操作并发避免并发读写非线程安全容器

关键点:

  • JDK 1.8 的 HashMap 仍非线程安全(尾插法解决死循环,但其他问题仍存在)。
  • 永远不要在多线程中直接使用 HashMap,优先选择 ConcurrentHashMap

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/72694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

告别GitHub连不上!一分钟快速访问方案

一、当GitHub抽风时&#xff0c;你是否也这样崩溃过&#xff1f; &#x1f621; npm install卡在node-sass半小时不动&#x1f62d; git clone到90%突然fatal: early EOF&#x1f92c; 改了半天hosts文件&#xff0c;第二天又失效了... 根本原因&#xff1a;传统代理需要复杂…

【软考-架构】2.1、操作系统概述-进程管理-同步互斥

✨资料&文章更新✨ GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目录 操作系统知识操作系统概述进程组成和状态&#x1f4af;考试真题前趋图进程资源图&#x1f4af;考试真题问题1问题2 ✨【重点】进程同步与互斥✨&#x1f4af;考试真题问题…

基于开源库编写MQTT通讯

目录 1. MQTT是什么&#xff1f;2. 开发交互UI3. 服务器核心代码4. 客户端核心代码5. 消息订阅与发布6. 通讯测试7. MQTT与PLC通讯最后. 核心总结 1. MQTT是什么&#xff1f; MQTT&#xff08;Message Queuing Terlemetry Transport&#xff09;消息队列遥测协议&#xff1b;是…

在VScode下配置C/C++环境(tasks.json、launch.json、c_cpp_properties.json)

文章目录 1. tasks.json、launch.json配置文件中参数(属性)的说明2. tasks.json介绍3. launch.json介绍4. 直接生成tasks.json、launch.json配置文件的另外一种方式5. c_cpp_properties.json介绍6. 运行多个C/C文件7. 命令行方式编译C 1. tasks.json、launch.json配置文件中参数…

ORB-SLAM2源码学习(六):相机跟踪(局部地图跟踪和关键帧创建)

目录 1.局部地图跟踪 1.1 更新局部关键帧UpdateLocalKeyFrames 1.2 更新局部地图点&#xff08;来自局部关键帧&#xff09;UpdateLocalPoints() 1.3 投影匹配 2. 对比四种跟踪方式以及使用的投影匹配 3.关键帧创建 3.1 判断是否需要创建新关键帧: NeedNewKeyFrame() 3…

PostgreSQL时间计算大全:从时间差到时区转换(保姆级教程)

一、时间计算的三大核心场景 当你遇到这些需求时&#xff0c;本文就是你的救星&#x1f31f;&#xff1a; 倒计时功能&#xff1a;计算活动剩余天数 用户行为分析&#xff1a;统计操作间隔时间 跨国系统&#xff1a;多时区时间统一管理 报表生成&#xff1a;自动计算同比/环…

Qt6.8.2创建WebAssmebly项目使用FFmpeg资源

Qt6新出了WebAssmebly功能&#xff0c;可以将C写的软件到浏览器中运行&#xff0c;最近一段时间正在研究这方便内容&#xff0c;普通的控件响应都能实现&#xff0c;今天主要为大家分享如何将FFmpeg中的功能应用到浏览器中。 开发环境&#xff1a;window11&#xff0c;Qt6.8.2…

DeepSeek V3 源码:从入门到放弃!

从入门到放弃 花了几天时间&#xff0c;看懂了DeepSeek V3 源码的逻辑。源码的逻辑是不难的&#xff0c;但为什么模型结构需要这样设计&#xff0c;为什么参数需要这样设置呢&#xff1f;知其然&#xff0c;但不知其所以然。除了模型结构以外&#xff0c;模型的训练数据、训练…

【leetcode hot 100 240】搜索二维矩阵Ⅱ

解法一&#xff1a;直接查找 class Solution {public boolean searchMatrix(int[][] matrix, int target) {for(int i0; i<matrix.length; i){for(int j0; j<matrix[0].length; j){if(matrix[i][j]>target){break;}if(matrix[i][j]target){return true;}}}return fal…

UE4 组件 (对话组件)

制作一个可以生成对话气泡&#xff0c;显示对话台词的简单组件。这个组件要的变量&#xff1a;台词&#xff08;外部传入&#xff09;。功能&#xff1a;开始对话&#xff08;生成气泡UI&#xff09; &#xff0c;结束对话。 一、对话组件创建 二、开始对话事件 1、注意这里获…

自动化同步多服务器数据库表结构

当项目每次进行版本升级的时候&#xff0c;如果在这次迭代中涉及表结构变更&#xff0c;需要将不同的生产环境下&#xff0c;都需要同步表结构的DDL语句&#xff0c;比较麻烦&#xff0c;而且还有可能忘记同步脚本&#xff0c;导致生产环境报错.... 该方案采用SpringBootMybat…

DeepSeek安全:AI网络安全评估与防护策略

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 本文基于现有的公开资料&#xff0c;从企业资深网络安全专家的视角&#xff0c;系统梳理DeepSeek技术在网络安全领域的潜在贡献与核心风险&#xff0c;并结合中国…

【论文笔记】Attentive Eraser

标题&#xff1a;Attentive Eraser: Unleashing Diffusion Model’s Object Removal Potential via Self-Attention Redirection Guidance Source&#xff1a;https://arxiv.org/pdf/2412.12974 收录&#xff1a;AAAI 25 作者单位&#xff1a;浙工商&#xff0c;字节&#…

【powerjob】 powerjobserver注册服务IP错误

1、问题&#xff1a;powerjobserver 4.3.6 的服务器上有多个网卡对应多个ip,示例 eth0 :IP1 &#xff0c;docker0:IP2 和worker 进行通信时 正确的应该时IP1 但是注册显示获取的确实IP2,导致 worker 通过ip2和server通信&#xff0c;网络不通&#xff0c;注册不上 2、解决方案 …

视频录像机视频通道是指什么

视频录像机的视频通道是指摄像机在监控矩阵或硬盘录像机设备上的视频输入的物理位置。 与摄像头数量关系&#xff1a;在视频监控系统中&#xff0c;有多少个摄像头就需要多少路视频通道&#xff0c;通道数量决定了视频录像机可接入摄像头的数量&#xff0c;一般硬盘录像机有4路…

面试150,数组 / 字符串

27. 移除元素 class Solution:def removeElement(self, nums: List[int], val: int) -> int:# 把不等于 val 的值移动到前面n len(nums)left 0for right in range(n):if nums[right] ! val:nums[left] nums[right]left 1return left26. 删除有序数组中的重复项 只保留 1…

【江科大STM32】TIM输入捕获模式PWMI模式测频率

一、输入捕获测频率 接线图&#xff1a; 测信号的输入引脚为PA6&#xff0c;信号从PA6进来&#xff0c;待测的PWM信号也是STM32自己生成的&#xff0c;输出引脚是PA0&#xff0c;所以接线这里直接用一根线将PA0引到PA6就可以了。 如果有信号发生器的话&#xff0c;也可以设置成…

湖仓一体化及冷、热、实时三级存储

一、湖仓一体化&#xff08;Lakehouse&#xff09; 湖仓一体化&#xff08;Lakehouse&#xff09;是数据湖&#xff08;Data Lake&#xff09;与数据仓库&#xff08;Data Warehouse&#xff09;的结合&#xff0c;旨在解决传统数据架构中数据孤岛、存储冗余、计算性能不足等问…

go切片定义和初始化

1.简介 切片是数组的一个引用&#xff0c;因此切片是引用类型&#xff0c;在进行传递时&#xff0c;遵守引用传递的机制。切片的使用和数组类似&#xff0c;遍历切片、访问切片的元素和切片的长度都一样。。切片的长度是可以变化的&#xff0c;因此切片是一个可以动态变化的数…

游戏引擎学习第138天

仓库:https://gitee.com/mrxiao_com/2d_game_3 资产&#xff1a;game_hero_test_assets_003.zip 发布 我们的目标是展示游戏运行时的完整过程&#xff0c;从像素渲染到不使用GPU的方式&#xff0c;我们自己编写了渲染器并完成了所有的工作。今天我们开始了一些新的内容&#…