对HashMap的理解(二):高并发下的HashMap
在分析hashmap高并发场景之前,我们要先搞清楚ReHash这个概念。ReHash是HashMap在扩容时的一个步骤。
HashMap的容量是有限的。当经过多次元素插入,使得HashMap达到一定饱和度时,Key映射位置发生冲突的几率会逐渐提高。这时候,HashMap需要扩展它的长度,也就是进行Resize。
影响发生Resize的因素有两个:
1.Capacity:HashMap的当前长度。上一篇曾经说过,HashMap的长度是2的幂。
2.LoadFactor:HashMap负载因子,默认值为0.75f。
衡量HashMap是否进行Resize的条件如下:
HashMap.Size >= Capacity * LoadFactor
Resize的两个步骤:
1.扩容
创建一个新的Entry空数组,长度是原数组的2倍。
2.ReHash
遍历原Entry数组,把所有的Entry重新Hash到新数组。为什么要重新Hash呢?因为长度扩大以后,Hash的规则也随之改变。
让我们回顾一下Hash公式:
index = HashCode(Key) & (Length - 1)
当原数组长度为8时,Hash运算是和111B做与运算;新数组长度为16,Hash运算是和1111B做与运算。Hash结果显然不同。
Resize前的HashMap:
Resize后的HashMap:
ReHash的Java代码如下:
/**
* Transfers all entries from current table to newTable.
*/
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;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
上述流程在单线程下执行是没有问题的,但在多线程下,HashMap并非线程安全的。下面在演示在多线程环境下,HashMap的ReHash操作可能带来什么样的问题之前,先说明在单线程下的执行情况。
在说明之前,先将ReHash的代码分为如下三部分:
(一)、单线程的情况下:
假设一个HashMap已经到了Resize的临界点:
1. 扩容
2. ReHash
首先遍历Entry4对象:e = Entry4
第8行:next = e.next; => next = null; //此时:e = Entry4, next = null
第12行:假设i = 1;
第13-15行:e.next = newTable[1]; => e.next = null;
newTable[1] = e; => newTable[1] = Entry4;
e = next; => e = null; //此时:e = null, next = null
Entry4放入newTable[1]的位置,结束while循环。开始遍历下一个对象:e = Entry3 第8行:next = e.next; => next = Entry2; //此时:e = Entry3, next = Entry2
第12行:假设i = 3;
第13-15行:e.next = newTable[3]; => e.next = null; //要插入的结点的next指向newTable[i]
newTable[3] = e; => newTable[3] = Entry3; //要插入的结点放在newTable[i]处
e = next; => e = Entry2; //此时:e = Entry2, next = Entry2
Entry3放入newTable[3]的位置,继续while循环,开始遍历下一个对象:e = Entry2 第8行:next = e.next; => next = Entry1; //此时:e = Entry2, next = Entry1
第12行:假设i = 3;
第13-15行:e.next = newTable[3]; => e.next = Entry3;
newTable[3] = e; => newTable[3] = Entry2;
e = next; => e = Entry1; //此时:e = Entry1, next = Entry1
Entry2放入newTable[3]的位置,且Entry2.next = Entry3,继续while循环,开始遍历下一个对象:e = Entry1 .....
(二)、多线程的情况下:
1. 假设一个HashMap已经到了Resize的临界点:
2. 此时有两个线程A和B,在同一时刻对HashMap进行Put操作:
3. 此时达到Resize条件,两个线程各自进行Rezie的第一步,也就是扩容:
4. 这时候,两个线程都走到了ReHash的步骤。
假如此时线程B遍历到Entry3对象,刚执行完第8行代码,线程就被挂起。对于线程B来说:
e = Entry3
next = Entry2
这时候线程A畅通无阻地进行着Rehash,当ReHash完成后,结果如下(图中的e和next,代表线程B的两个引用):
直到这一步,看起来没什么毛病。
5. 接下来线程B恢复,继续执行属于它自己的ReHash。(e = Entry3, next = Entry2)
//线程B继续执行
第12行:i = 3 //因为刚才线程A对于Entry3的hash结果也是3
第13-15行:e.next = newTable[3]; => e.next = null;
newTable[3] = e; => newTable[3] = Entry3;
e = next; => e = Entry2; //此时:e = Entry2, next = Entry2
Entry3放入线程B中newTable[3]的位置,继续while循环,开始遍历下一个对象:e = Entry2
整体情况如下:(线程A中e和next都指向Entry2,线程B只有Entry3)
6. 继续执行:
//继续while循环
第8行:next = e.next; => next = Entry3; //此时:e = Entry2, next = Entry3
第12行:i = 3;
第13-15行:e.next = newTable[3]; => e.next = Entry3;
newTable[3] = e; => newTable[3] = Entry2;
e = next; => e = Entry3; //此时:e = Entry3, next = Entry3
Entry2放入newTable[3]的位置,且Entry2.next = Entry3,继续while循环,开始遍历下一个对象:e = Entry3
整体情况如下:(线程A中e和next都指向Entry3,线程B有Entry2和Entry3)
7. 继续执行:
//继续while循环
第8行:next = e.next; => next = null; //此时:e = Entry3, next = null
第9行:i = 3;
第13-15行:e.next = newTable[i]; => e.next = Entry2;
newTable[3] = e; => newTable[3] = Entry3;
e = next; => e = null; //此时:e = null, next = null
Entry3放入newTable[3]的位置,且Entry3.next = Entry2,结束while循环。
整体情况如下:(链表出现了环形)
此时,问题还没有直接产生。当调用Get查找一个不存在的Key,而这个Key的Hash结果恰好等于3的时候,由于位置3带有环形链表,所以程序将会进入死循环!
总结:
1.Hashmap在插入元素过多的时候需要进行Resize,Resize的条件是
HashMap.Size >= Capacity * LoadFactor。
2.Hashmap的Resize包含扩容和ReHash两个步骤,ReHash在并发的情况下可能会形成链表环。
来自公众号:
对HashMap的理解(二):高并发下的HashMap的更多相关文章
- JDK1.7 ConcurrentHashMap--解决高并发下的HashMap使用问题
高并发下也可以使用HashTable .Collections.synchronizedMap因为他们是线程安全的,但是却牺牲了性能,无论是读操作.写操作都是给整个集合加锁,导致同一时间内其他操作均为 ...
- JDK1.7 高并发下的HashMap
HashMap的容量是有限的.当经过多次元素插入,使得HashMap达到一定饱和度时,Key映射位置发生冲突的几率会逐渐提高. 这时候,HashMap需要扩展它的长度,也就是进行Resize. 影响发 ...
- 漫画:高并发下的HashMap
这一期我们来讲解高并发环境下,HashMap可能出现的致命问题. HashMap的容量是有限的.当经过多次元素插入,使得HashMap达到一定饱和度时,Key映射位置发生冲突的几率会逐渐提高. 这时候 ...
- 高并发下,HashMap会产生哪些问题?
HashMap在高并发环境下会产生的问题 HashMap其实并不是线程安全的,在高并发的情况下,会产生并发引起的问题: 比如: HashMap死循环,造成CPU100%负载 触发fail-fast 下 ...
- 高并发下的HashMap,ConcurrentHashMap
参照: http://mp.weixin.qq.com/s/dzNq50zBQ4iDrOAhM4a70A http://mp.weixin.qq.com/s/1yWSfdz0j-PprGkDgOomh ...
- 高并发下的Java数据结构(List、Set、Map、Queue)
由于并行程序与串行程序的不同特点,适用于串行程序的一些数据结构可能无法直接在并发环境下正常工作,这是因为这些数据结构不是线程安全的.本节将着重介绍一些可以用于多线程环境的数据结构,如并发List.并发 ...
- HashMap 在高并发下引起的死循环
HashMap 基本实现(JDK 8 之前) HashMap 通常会用一个指针数组(假设为 table[])来做分散所有的 key,当一个 key 被加入时,会通过 Hash 算法通过 key 算出这 ...
- HashMap高并发下存在的问题
原文链接:https://blog.csdn.net/bjwfm2011/article/details/81076736 1.什么是HashMap? HashMap底层原理 HashMap是存储键值 ...
- Java高并发下多线程编程
1.创建线程 Java中创建线程主要有三种方式: 继承Thread类创建线程类: 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此也把run方法称为 ...
随机推荐
- Spring学习(十五)----- Spring AOP通知实例 – Advice
Spring AOP(面向方面编程)框架,用于在模块化方面的横切关注点.简单得说,它只是一个拦截器拦截一些过程,例如,当一个方法执行,Spring AOP 可以劫持一个执行的方法,在方法执行之前或之后 ...
- Jupyter安装及运行
一.安装(来自http://jupyter.org/install) Ctrl+Alt+T(Manjaro系统),打开控制台,su进入root权限 输入如下命令: python3 -m pip ins ...
- Qt-网易云音乐界面实现-4 实现推荐列表和我的音乐列表,重要在QListWidget美化
来标记下这次我么实现的部分 这次我们来是试下这部分功能,来对比一下,左边是原生,右面是我写的,按着模仿的海可以哈,就有有的资源不是一样了,因为我连抠图都懒得扣了了 好了,现在就是我的是先过程了,主要教 ...
- InsertionSort 直接插入排序(java)
排序思想: 相当于一堆数字,一开始先取出2个数排序,2个数排好序之后,再从一堆数字里面取一个数排序,直到结束伪代码: INSERTION_SORT(A) for j = 2 to A.length k ...
- C++ 学习笔记 变量和基本类型(一)
C++ 学习笔记 一.变量和基本类型概述 类型是所有程序的基础.类型告诉我们数据代表什么意思以及可以对数据执行哪些操作. c++基本类型: 字符型 整型 浮点型 c++ 还提供了可用于自定义数据类型的 ...
- Oracle数据库之分组查询及排序
分组查询:使用 group by 来设置分组,把该列具有相同值的多条记录当成一组记录来处理,然后只会输出一条记录,得到的结果会默认使用升序的方式进行排列. 规则: (1)如果使用了分组函数,或者是 g ...
- 虚拟机中安装MAC OS X教程(适用所有电脑方法,特别是cpu不支持硬件虚拟化的电脑)
前言 之前写了一篇在Windows上搭建Object-C开发环境,并且写了一个HelloWorld程序.但真正开发苹果软件是在MAC OS X系统中(以下简称OSX)中.买不起MacBook,也没有O ...
- 基于WebSocket协议的性能测试
互联网应用时代,用户获取信息的方式从传统媒体到新媒体,信息时效性对通信技术要求越来越高, HTTP协议已经不能适用.于是WebSocket出现了,它实现浏览器与服务器的全双工通信,服务端主动向客户端发 ...
- Cocos2dx源码赏析(1)之启动流程与主循环
Cocos2dx源码赏析(1)之启动流程与主循环 我们知道Cocos2dx是一款开源的跨平台游戏引擎,而学习开源项目一个较实用的办法就是读源码.所谓,"源码之前,了无秘密".而笔者 ...
- Windows 本地文件搜索神器
Wox: Windows 本地文件搜索神器 下载地址: https://github.com/Wox-launcher/Wox 注: Wox只能搜索C盘下的文件,所以需要结合everything 如果 ...