0

0

详解HashMap的线程安全问题_多线程下循环链表(JDK 1.7)与数据覆盖

P粉602998670

P粉602998670

发布时间:2026-02-13 11:23:14

|

346人浏览过

|

来源于php中文网

原创

jdk 1.7 hashmap 多线程 put 死循环的根本原因是扩容时头插法与非原子操作结合导致链表成环;jdk 1.8 改为尾插并引入红黑树,仅缓解死循环但不保证线程安全。

详解hashmap的线程安全问题_多线程下循环链表(jdk 1.7)与数据覆盖

为什么 JDK 1.7 的 HashMap 多线程 put 会触发死循环

根本原因是扩容时的头插法 + 非原子性操作,在多线程竞争下可能让链表节点形成环。这不是偶发 bug,而是确定性可复现的逻辑缺陷。

典型现象是 CPU 突升 100%,线程卡在 get()put() 的遍历逻辑里无限循环——因为 Entry.next 指向了自己或前面的节点。

  • 触发条件:多个线程同时触发扩容(size > threshold),且操作的是同一个桶(bucket)上的链表
  • 关键动作:旧链表逐个摘下节点,用头插法塞进新数组对应位置 —— 这一步在多线程下会交叉执行
  • 结果:比如 A 线程刚把节点 X 插到新桶头,B 线程紧接着把 X 的原 next 节点 Y 插到同个桶头,而 Y 的 next 又指回 X,环就形成了

JDK 1.7 vs JDK 1.8 的链表处理差异直接影响线程安全表现

JDK 1.8 把头插改成了尾插,单次 put 不再反转链表顺序,但这只是「缓解」死循环,并不等于线程安全。

真正改变的是:JDK 1.8 在链表过长时会转为红黑树(treeifyBin()),但树化过程本身不是原子的;而且所有非 final 字段(如 sizemodCount)依然无锁更新,多线程下仍会丢数据、读到脏值。

腾讯云AI代码助手
腾讯云AI代码助手

基于混元代码大模型的AI辅助编码工具

下载
  • put() 操作在 JDK 1.8 中仍是非原子的:两个线程对同一 key 调用 put(),后写会覆盖前写,无任何提示
  • size() 返回的可能是错的:它只是简单返回 volatile 字段,不加锁也不重算,高并发下误差可达 10% 以上
  • 即使没死循环,get() 也可能读到 null 或旧值,因为节点字段(如 value)没用 volatile 修饰(JDK 1.8 中部分字段加了,但整体结构未保证可见性)

什么情况下你以为用了 ConcurrentHashMap 其实还是线程不安全

常见错觉是“只要换掉 HashMap 就万事大吉”,但 ConcurrentHashMap 的线程安全是有边界的,越界操作照样出问题。

  • 复合操作非原子:比如 if (!map.containsKey(k)) map.put(k, v),中间可能被其他线程插入同 key,导致重复写或逻辑错乱
  • 迭代器弱一致性:entrySet().iterator() 不抛 ConcurrentModificationException,但可能漏掉刚 put 的项,或重复看到某项 —— 它不保证快照视图
  • 默认并发级别(concurrencyLevel=16)只控制分段锁粒度,不是线程数上限;实际锁的是桶数组的某一段,若所有 key 都哈希到同一段,性能退化成类似 Hashtable

替代方案选型:别只盯着 Map 接口,要看真实场景需求

线程安全不是非黑即白的选择题,得看你要保什么:是不能丢数据?不能死循环?还是不能读到中间态?

  • 纯读多写少 + 可接受短暂不一致 → ConcurrentHashMap(JDK 1.8+)够用,注意避开复合操作
  • 写极少、读极多、要求强一致性 → Collections.unmodifiableMap(new HashMap()) 配合外部同步,或用 CopyOnWriteArrayList 思路自建不可变快照
  • 需要原子复合操作(如计数、累加)→ 直接上 LongAdderAtomicInteger,或用 ConcurrentHashMap.computeIfAbsent() 这类 CAS 封装方法
  • 涉及跨 key 逻辑(如转账、库存扣减)→ 别硬靠 Map,该上数据库事务或分布式锁就上,Map 不是通用同步原语

最常被忽略的一点:很多所谓“线程安全问题”,其实源于把 Map 当作状态协调中心来用,而它天生就不适合做这个角色。真要共享状态,优先考虑明确的同步边界或专用工具类。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

387

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

244

2023.10.07

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

244

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

706

2024.03.01

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

807

2023.08.22

c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

70

2025.10.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1417

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

339

2025.10.17

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

15

2026.02.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号