在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,阻塞:

  1. 线程一开始执行,并执行完代码行1:

  1. 线程一执行代码行2,假设我们在扩容时两个节点新索引值为5:
e.next = newTable[5];

由于此时newTable[5]==null所以就相当于e.next=null,也就是A.next=null。


4. 线程一执行代码行3:


5. 线程一执行代码行4:

  1. 线程一再次进入循环执行代码行一,此时线程一中next变量指向null:


7. 线程一执行代码行2:


8. 线程一执行代码行3:

  1. 线程一执行代码行4:


此时线程一的扩容工作已经进行完毕,我们开始回到线程二。

  1. 线程二执行代码行2:

    e.next = newTable[i];

由于线程二的newTable[i]=null,而e.next也就是A.next本来就为空,所以这句话在此时并未对结构造成实质影响。

  1. 线程二执行代码行3:


12. 线程二执行代码行4:

  1. 线程二再次循环执行代码行1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iE9kg19T-1573800259936)(…/images/29.png)]

  1. 线程二执行代码行2:

    本行代码对结构无影响

  1. 线程二执行代码行3:

  2. 线程二执行代码行4:

  1. 线程二执行代码行1:


18. 线程二执行代码行2(成环):

此时A、B两节点之间就会形成环形结构。

  1. 线程二执行代码行3:

  2. 线程二执行代码行4:


在JDK8中由于采取了不同的扩容机制,所以在JDK8中不会出现扩容死锁问题。但是这并不意味着JDK8的HashMap是线程安全的。

图解JDK7及其早期版本HashMap扩容死锁问题的更多相关文章

  1. JDK7与JDK8中HashMap的实现

    JDK7中的HashMap HashMap底层维护一个数组,数组中的每一项都是一个Entry transient Entry<K,V>[] table; 我们向 HashMap 中所放置的 ...

  2. 关于JDK1.8 HashMap扩容部分源码分析

    今天回顾hashmap源码的时候发现一个很有意思的地方,那就是jdk1.8在hashmap扩容上面的优化. 首先大家可能都知道,1.8比1.7多出了一个红黑树化的操作,当然在扩容的时候也要对红黑树进行 ...

  3. 将“早期版本的Windows”改名

    将“早期版本的Windows”改名,并修改系统等待时间 问题描述:       先装Windows XP,再装Windows 7,启动菜单会出现“早期版本的Windows”与“Windows 7”两个 ...

  4. 安装了SQL2005再安装SQL 2008R2,提示此计算机上安装了 Microsoft Visual Studio 2008 的早期版本和检查是否安装了 SQL Server 2005 Express 工具的解决方案

    工作电脑上安装了SQL 2005, 但是客户电脑上安装的是SQL 2008R2,有时候连接他们的库调试没法连接,很不方便.然后又安装了个SQL2008 R2,期间遇到这两个问题,网上搜索了一下收到了解 ...

  5. SQL SERVER安装提示“安装了 Microsoft Visual Studio 2008 的早期版本

    工作共遇到的问题记录: 安装Sql Server 2008 R2时提示错误:“此计算机上安装了 Microsoft Visual Studio 2008 的早期版本.请在安装 SQL Server 2 ...

  6. 【转】预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)

    用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...

  7. VS2005 MFC 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)

    当 Visual C++ 项目启用了预编译头 (Precompiled header) 功能时,如果项目中同时混合有 .c 和 .cpp 源文件,则可能收到 C1853 编译器错误:fatal err ...

  8. Java 垃圾回收机制(早期版本)

    Java 垃圾回收机制在我们普通理解来看,应该视为一种低优先级的后台进程来实现的,其实早期版本的Java虚拟机并非以这种方式实现的. 先从一种很简单的垃圾回收方式开始. 引用计数 引用计数是一种简单但 ...

  9. [转帖]Windows 10 部分早期版本已完全停止技术支持服务

    Windows 10 部分早期版本已完全停止技术支持服务 2019-4-12 01:27| 发布者: cjy__05| 查看: 10186| 评论: 47|来自: pcbeta 收藏分享 转帖来源:h ...

  10. 面试笔记--HashMap扩容机制

    转载请注明出处 http://www.cnblogs.com/yanzige/p/8392142.html 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. 存放新 ...

随机推荐

  1. Qt调用摄像头一,基础版

    本示例,为纯Qt调用摄像头,功能比较简单,打开摄像头,设置参数,拍照 涉及到的功能有: 获取摄像头列表 获取摄像头分辨率 获取摄像头帧率 获取摄像头支持的视频模式 设置摄像头参数 拍照 此版本的缺点是 ...

  2. easyExcel合并数据导出(一对多)

    语言 java 框架 ssm 需求 :看图  也是导出效果 数据库查询为(关系为一对多) 一个学生对应多个课程 实现步骤 1.实体类配置, 建议单独写个实体用来导出使用() 学生信息字段正常配置  , ...

  3. Java+HTML预习笔记_20140610

    1.HTML <img> 标签 HTML <img> 标签 实例 在下面的例子中,我们在页面中插入一幅 W3School 的工程师在上海鲜花港拍摄的郁金香照片: <img ...

  4. button submit你以为你阻止了默认事件?

    前言 先解决掉一个误区: 很多人写button的时候,就这样写: <button><botton> 你认为就是默认的submit的时候,这时候就可能出问题了. 当然之所以你没有 ...

  5. docker 应用篇————docker-compose[十九]

    前言 简单介绍一下docker compose. 正文 首先进行下载一下. sudo curl -L "https://github.com/docker/compose/releases/ ...

  6. modbus通信案例简单介绍

    介绍: 1.仪表等其他智能设备的modbus通信协议,确定其内部功能码地址.以型号U-MIK-P350-SCN2的杭州美控公司的压力变送器为例.查看对应手册20页.2.PLC端的编程配置.以西门子s7 ...

  7. esp8266,arduino,网页显示dht11温湿度,控制继电器开关,局域网智能家居

    不说了,上代码,用arduino实现esp8266代码 #include <ESP8266WiFi.h> #include <WiFiClient.h> #include &l ...

  8. 微信小程序,本地和真机测试都是好的,但体验版扫码显示空白页

    大概率是缓存导致的,删除掉你手机上的小程序[开发版] 和 [体验版],然后再扫码进入体验版就好了.

  9. 对于小程序canvas在某些情况下touchmove 不能触发导致的签名不连续替代方案(企微)

    1.问题 微信开放社区链接 尝试过新版canvas,在企业微信中签名依然是依然断触,有问题的手机是iphoe15,系统版本以及企微版本微信版本均与签名正常的手机一致,但是那个手机就是无法正常签字,在微 ...

  10. 力扣1083(MySQL)-销售分析Ⅲ(简单)

    题目: Table: Product Table: Sales 编写一个SQL查询,报告2019年春季才售出的产品.即仅在2019-01-01至2019-03-31(含)之间出售的商品. 以 任意顺序 ...