图解JDK7及其早期版本HashMap扩容死锁问题
在JDK7及其早期版本中HashMap在多线程环境下会发生扩容死锁的问题。
HashMap中在创建时默认会有16个桶,有一个默认加载因子0.75,如果Map中的Entry数量达到阈值(16*0.75)就会进行扩容,将原来的桶的数量扩展至原来的两倍,而在多线程环境下JDK7的HashMap会产生扩容死锁的问题。
下列方法是Map进行扩容的核心方法:
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
/**
* 将所有Entry从当前表转移到新表。
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;//1
//计算Hash,然后计算新数组中的索引值
/*if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);*/
e.next = newTable[i];//2
newTable[i] = e;//3
e = next;//4
}
}
}
我们大致可以将Entry的重散列看成四步:
- Entry<K,V> next = e.next;
- e.next = newTable[i];
- newTable[i] = e;
- e = next;
我们分别将上面四行代码编号为:1、2、3、4
现在我们来重现多线程情况下扩容死锁的情况,为了方便演示,我们将原始Map的桶的数量定为4,扩容后容量翻倍变为8,原Map中有两个Entry需要重散列,分别为:A、B。
- 线程二执行完代码行1,阻塞:
- 线程一开始执行,并执行完代码行1:
- 线程一执行代码行2,假设我们在扩容时两个节点新索引值为5:
e.next = newTable[5];
由于此时newTable[5]==null
所以就相当于e.next=null,也就是A.next=null。
4. 线程一执行代码行3:
5. 线程一执行代码行4:
- 线程一再次进入循环执行代码行一,此时线程一中next变量指向null:
7. 线程一执行代码行2:
8. 线程一执行代码行3:
- 线程一执行代码行4:
此时线程一的扩容工作已经进行完毕,我们开始回到线程二。
线程二执行代码行2:
e.next = newTable[i];
由于线程二的newTable[i]=null,而e.next也就是A.next本来就为空,所以这句话在此时并未对结构造成实质影响。
- 线程二执行代码行3:
12. 线程二执行代码行4:
- 线程二再次循环执行代码行1:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iE9kg19T-1573800259936)(…/images/29.png)]
线程二执行代码行2:
本行代码对结构无影响
线程二执行代码行3:
线程二执行代码行4:
- 线程二执行代码行1:
18. 线程二执行代码行2(成环):
此时A、B两节点之间就会形成环形结构。
线程二执行代码行3:
线程二执行代码行4:
在JDK8中由于采取了不同的扩容机制,所以在JDK8中不会出现扩容死锁问题。但是这并不意味着JDK8的HashMap是线程安全的。
图解JDK7及其早期版本HashMap扩容死锁问题的更多相关文章
- JDK7与JDK8中HashMap的实现
JDK7中的HashMap HashMap底层维护一个数组,数组中的每一项都是一个Entry transient Entry<K,V>[] table; 我们向 HashMap 中所放置的 ...
- 关于JDK1.8 HashMap扩容部分源码分析
今天回顾hashmap源码的时候发现一个很有意思的地方,那就是jdk1.8在hashmap扩容上面的优化. 首先大家可能都知道,1.8比1.7多出了一个红黑树化的操作,当然在扩容的时候也要对红黑树进行 ...
- 将“早期版本的Windows”改名
将“早期版本的Windows”改名,并修改系统等待时间 问题描述: 先装Windows XP,再装Windows 7,启动菜单会出现“早期版本的Windows”与“Windows 7”两个 ...
- 安装了SQL2005再安装SQL 2008R2,提示此计算机上安装了 Microsoft Visual Studio 2008 的早期版本和检查是否安装了 SQL Server 2005 Express 工具的解决方案
工作电脑上安装了SQL 2005, 但是客户电脑上安装的是SQL 2008R2,有时候连接他们的库调试没法连接,很不方便.然后又安装了个SQL2008 R2,期间遇到这两个问题,网上搜索了一下收到了解 ...
- SQL SERVER安装提示“安装了 Microsoft Visual Studio 2008 的早期版本
工作共遇到的问题记录: 安装Sql Server 2008 R2时提示错误:“此计算机上安装了 Microsoft Visual Studio 2008 的早期版本.请在安装 SQL Server 2 ...
- 【转】预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...
- VS2005 MFC 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)
当 Visual C++ 项目启用了预编译头 (Precompiled header) 功能时,如果项目中同时混合有 .c 和 .cpp 源文件,则可能收到 C1853 编译器错误:fatal err ...
- Java 垃圾回收机制(早期版本)
Java 垃圾回收机制在我们普通理解来看,应该视为一种低优先级的后台进程来实现的,其实早期版本的Java虚拟机并非以这种方式实现的. 先从一种很简单的垃圾回收方式开始. 引用计数 引用计数是一种简单但 ...
- [转帖]Windows 10 部分早期版本已完全停止技术支持服务
Windows 10 部分早期版本已完全停止技术支持服务 2019-4-12 01:27| 发布者: cjy__05| 查看: 10186| 评论: 47|来自: pcbeta 收藏分享 转帖来源:h ...
- 面试笔记--HashMap扩容机制
转载请注明出处 http://www.cnblogs.com/yanzige/p/8392142.html 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. 存放新 ...
随机推荐
- "鸿蒙生态专家面对面"三月专场等你前来!
- 重新点亮linux 命令树————网络管理[十一二]
前言 简单整理一下网络管理. 正文 网络管理需要掌握: 网络状态查看 网络配置 路由命令 网络故障排除 网络服务管理 常用网络配置文件 网络状态的查看: 1.net-tools ---->1.i ...
- 低成本FPGA的MIPI测试GOWIN和LATTICE CROSSLINK
本次实验MIPI屏,2.0寸,分辨率是240*320 RGB888, 接口如下: 接上IO就是RST和MIPI的时钟和数据接口,另外就是电源和地. 一:GOWIN的测试方案 Gowin的案例中,首先是 ...
- 如何使用鞋厂ERP等企业管理软件提高企业运营整体效率?
ERP把企业客户需求.市场规划.产品研发.内部制造等活动以及供应商的资源整合在一起,形成企业一个完整的产业链和供应链,通过企业多个环节的无缝链接和整体运作来提高企业运营整体效率: (1) . 对整个产 ...
- 报错ORA-01830: date format picture ends before converting entire input string
报错ORA-01830: date format picture ends before converting entire input string 原语句 select to_char(to_da ...
- Advanced .Net Debugging 7:托管堆与垃圾收集
一.简介 这是我的<Advanced .Net Debugging>这个系列的第七篇文章.这篇文章的内容是原书的第二部分的[调试实战]的第五章,这一章主要讲的是从根本上认识托管堆和垃圾回收 ...
- OpenKruise v1.3:新增自定义 Pod Probe 探针能力与大规模集群性能显著提升
简介: 在版本 v1.3 中,OpenKruise 提供了新的 CRD 资源 PodProbeMarker,改善了大规模集群的一些性能问题,Advanced DaemonSet 支持镜像预热,以及 C ...
- ARMS实践|日志在可观测场景下的应用
简介: 在实际生产中,通过灵活组合文内几种使用方式,运维团队可以很好地排除日常观测.故障定位过程中的干扰因素,更快的定界甚至定位问题根因. 作者:陈陈 日志在可观测场景下的应用 随着 IT 架 ...
- 优化搜索排序结果从而“ 提升CTR、CVR业务指标”
简介: 搭建搜索功能不难,难的是如何提高搜索质量,帮助用户快速找到心中所想的内容或商品,那么搜索结果的相关性排序则是影响用户体验最关键的一环,本文通过阿里云开放搜索电商行业解决方案和大家聊一聊如何优化 ...
- Go 调用 Java 方案和性能优化分享
简介: 一个基于 Golang 编写的日志收集和清洗的应用需要支持一些基于 JVM 的算子. 作者 | 响风 来源 | 阿里技术公众号 一 背景 一个基于 Golang 编写的日志收集和清洗的应 ...