news 2026/4/13 13:36:20

Java HashMap 多线程环境下的死链问题详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java HashMap 多线程环境下的死链问题详解

HashMap在多线程环境下使用时存在死链问题。让我详细解释一下这个问题的原因以及如何规避:

问题原因分析

在JDK 7及之前版本的HashMap中,resize时采用的是头插法重新排列链表,这会导致链表顺序反转:

// JDK 7的resize代码片段voidtransfer(Entry[]newTable){Entry[]src=table;intnewCapacity=newTable.length;for(intj=0;j<src.length;j++){Entry<K,V>e=src[j];if(e!=null){src[j]=null;do{Entry<K,V>next=e.next;// 关键点:线程A在这里读取nextinti=indexFor(e.hash,newCapacity);e.next=newTable[i];// 头插法:新节点插入到链表头部newTable[i]=e;e=next;}while(e!=null);}}}

死链产生过程:

  1. 线程A和B同时进行resize
  2. 线程A执行到Entry<K,V> next = e.next;后被挂起
  3. 线程B完成resize,链表顺序被反转
  4. 线程A恢复执行,此时它的next指向的是反转前的下一个节点
  5. 形成环形链表,导致后续操作无限循环

解决方案

1. 使用线程安全的集合类

ConcurrentHashMap(推荐)
// Java 8+ 推荐ConcurrentHashMap<String,Object>concurrentMap=newConcurrentHashMap<>();// 指定初始容量和并发级别ConcurrentHashMap<String,Object>map=newConcurrentHashMap<>(16,0.75f,16);

特点:

  • JDK 8+采用CAS + synchronized,性能更好
  • 分段锁(JDK 7)或桶级锁(JDK 8+)
  • 读写操作大部分情况下不需要全局锁
Collections.synchronizedMap
Map<String,Object>synchronizedMap=Collections.synchronizedMap(newHashMap<>());

特点:对整个map加锁,性能较差

2. 显式加锁控制

// 使用ReentrantLockpublicclassSafeHashMap<K,V>{privatefinalMap<K,V>map=newHashMap<>();privatefinalReentrantLocklock=newReentrantLock();publicVput(Kkey,Vvalue){lock.lock();try{returnmap.put(key,value);}finally{lock.unlock();}}publicVget(Kkey){lock.lock();try{returnmap.get(key);}finally{lock.unlock();}}}

3. 使用其他并发数据结构

CopyOnWrite(适合读多写少)
CopyOnWriteArrayList<String>list=newCopyOnWriteArrayList<>();CopyOnWriteArraySet<String>set=newCopyOnWriteArraySet<>();
ConcurrentLinkedQueue/Deque
ConcurrentLinkedQueue<String>queue=newConcurrentLinkedQueue<>();ConcurrentLinkedDeque<String>deque=newConcurrentLinkedDeque<>();

4. 使用读写锁(ReadWriteLock)

publicclassReadWriteMap<K,V>{privatefinalMap<K,V>map=newHashMap<>();privatefinalReadWriteLocklock=newReentrantReadWriteLock();publicVput(Kkey,Vvalue){lock.writeLock().lock();try{returnmap.put(key,value);}finally{lock.writeLock().unlock();}}publicVget(Kkey){lock.readLock().lock();try{returnmap.get(key);}finally{lock.readLock().unlock();}}}

版本差异说明

JDK 8+的改进

JDK 8中的HashMap在resize时:

  1. 保持了链表元素的顺序(尾插法)
  2. 但仍然不是线程安全的,只是减少了死链的概率
  3. 仍可能出现数据丢失、size计算错误等问题
// JDK 8使用尾插法if(loTail!=null){loTail.next=null;newTab[j]=loHead;// 保持原顺序}

最佳实践建议

1. 单线程环境

  • 使用HashMap(性能最好)

2. 多线程环境

  • 读写都频繁ConcurrentHashMap
  • 写少读多CopyOnWrite系列
  • 需要强一致性Collections.synchronizedMap或显式加锁
  • 高并发计数LongAdderConcurrentHashMap的原子操作

3. 特定场景

// 统计计数ConcurrentHashMap<String,LongAdder>counter=newConcurrentHashMap<>();counter.computeIfAbsent(key,k->newLongAdder()).increment();// 缓存实现ConcurrentHashMap<String,Future<T>>cache=newConcurrentHashMap<>();

4. 代码示例:线程安全的缓存

publicclassThreadSafeCache<K,V>{privatefinalConcurrentHashMap<K,V>cache=newConcurrentHashMap<>();privatefinalConcurrentHashMap<K,Future<V>>futures=newConcurrentHashMap<>();publicVget(Kkey,Callable<V>loader)throwsException{Vvalue=cache.get(key);if(value==null){Future<V>future=futures.computeIfAbsent(key,k->CompletableFuture.supplyAsync(()->{try{returnloader.call();}catch(Exceptione){thrownewRuntimeException(e);}}));value=future.get();cache.put(key,value);futures.remove(key);}returnvalue;}}

总结

方案线程安全性能适用场景
HashMap不安全最高单线程环境
ConcurrentHashMap安全高并发读写
Collections.synchronizedMap安全简单同步需求
显式加锁安全复杂同步逻辑
CopyOnWrite安全读高写低读多写少

核心建议:

  • 在多线程环境下,永远不要使用HashMap
  • 首选ConcurrentHashMap,它在绝大多数场景下都能提供良好的性能和线程安全
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/11 8:37:15

Conda环境导入导出:跨平台迁移PyTorch项目

Conda环境导入导出&#xff1a;跨平台迁移PyTorch项目 在深度学习项目的日常开发中&#xff0c;你是否遇到过这样的场景&#xff1a;本地训练一切正常&#xff0c;模型跑得飞快&#xff0c;信心满满地把代码推到服务器上准备大规模训练&#xff0c;结果一运行就报错——torch.…

作者头像 李华
网站建设 2026/4/7 14:16:23

如何在云服务器上部署PyTorch-CUDA环境?详细图文教程

如何在云服务器上部署 PyTorch-CUDA 环境&#xff1f;—— 从零到训练的实战指南 在深度学习项目中&#xff0c;最让人头疼的往往不是模型设计本身&#xff0c;而是环境搭建&#xff1a;CUDA 驱动装不上、PyTorch 版本不兼容、cuDNN 缺失……这些“环境陷阱”常常让开发者耗费…

作者头像 李华
网站建设 2026/4/4 20:05:54

【视频】GStreamer+WebRTC(五):通过修改SDP改变webrtc数据流单双方向

1、问题描述 在设备终端和人机交互终端使用webrtc实现视频和通话时,设备端不需要显示图像,只播放声音即可。 这时需要修改发送的SDP,比如将 sendrecv 改为 sendonly,如何操作呢? 2、解决方法 2.1 修改SDP 比如:在 webrtcbin 创建了offer SDP后,会发送 create-offer …

作者头像 李华
网站建设 2026/4/10 17:33:16

内网穿透的应用-服务器卡成 PPT?Netdata 帮你实时抓出 拖后腿 的进程

文章目录 前言1.关于Netdata**2.本地部署Netdata**3.使用Netdata4.cpolar内网穿透工具安装5.创建远程连接公网地址6.固定Netdata公网地址 前言 Netdata 是一款轻量级服务器监控工具&#xff0c;能实时展示 CPU、内存、网络带宽等硬件指标&#xff0c;还能追踪进程资源占用情况…

作者头像 李华
网站建设 2026/4/8 13:56:47

Jupyter Notebook密码保护设置:防止未授权访问

Jupyter Notebook密码保护设置&#xff1a;防止未授权访问 在云计算与远程开发日益普及的今天&#xff0c;数据科学家和AI工程师越来越依赖Jupyter Notebook进行模型实验、数据分析和教学演示。它以交互式Web界面打破了传统脚本开发的壁垒&#xff0c;让代码执行、结果可视化和…

作者头像 李华
网站建设 2026/3/26 22:12:17

自考必看!9个高效降AI率工具推荐,轻松应对查重难题

自考必看&#xff01;9个高效降AI率工具推荐&#xff0c;轻松应对查重难题 AI降重工具&#xff1a;自考论文的高效护航者 在自考论文写作过程中&#xff0c;越来越多的学生开始关注“AI生成内容检测率”&#xff08;AIGC率&#xff09;的问题。随着高校对学术规范要求的不断提高…

作者头像 李华