HashMap 为何会致使 CPU 100%?文章看不懂?来看这个视频吧!——面试突击 006 期

哈喽,你们好,我是老王,欢迎来到 Java 面试突击,咱们今天来开始第 6 期的内容。
本期的问题是:HashMap 为什会致使 CPU 运行 100%?这是一个比较常见的经典问题了,可是有不少人读者朋友给我反馈,尼玛,看文章根本看不懂啊?Sun 公司都不知道这个问题的缘由吧?不,是 Oracle 公司都不知道这个问题的缘由吧?面试官怕也不知道这个的答案吧?
咳咳,做为一个很正经的面试官,我以为这个问题一点都不重要,重要的是你不知道答案啊。好的,下一位面试者请进,您先回去等通知吧。
为了不这种尴尬的事情发生,今天咱们来好好聊一下这个问题,毕竟技能再手,才能吊打面试官不是?
正文面试

这个问题相关的知识点,有如下几个:
H```
ashMap 的底层数据结构是什么?
什么是哈希碰撞?如何该解决这个问题?
什么是扩展因子?它有什么用?
还有对 HashMap 源码的理解,为何 HashMap 会致使死循环?数组

视频版答案

视频内容以下:

图文答案

1.HashMap 的底层数据结构

先来讲 HashMap 的底层数据结构,看过 HashMap 的源码咱们就会发现,JDK 1.7 和 JDK 1.8 HashMap 的组成是不一样的,JDK 1.7 HashMap 的组成是数组 + 链表的形式,而 JDK 1.8 新增了红黑树的数据结构,当 HashMap 中的链表长度大于 8 时,链表结构就会转换为红黑树,以下图所示:
![](https://s4.51cto.com/images/blog/202007/31/4e006f8b3e8d00bc4d156443365473de.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

2.哈希碰撞及解决方案

所谓的哈希碰撞指的是不一样的值,通过哈希以后获得的值确是相同的,这种状况就叫作哈希碰撞或哈希冲突。解决哈希碰撞的经常使用方法是:开放定址法和链表地址法,而 HashMap 采用的就是链表地址法。它的实现原理就是将 HashMap 中相同的哈希值以链表的形式存储起来。
3.扩展因子

扩展因子也叫加载因子或负载因子是 HashMap 中的一个属性,以下图所示:假如数组的默认长度为 10,扩展因子为 0.5,那么当数组超过 10*0.5=5 个时,HashMap 就会扩容为以前容量的两倍,因此说扩展因子就是用来断定 HashMap 是否知足扩容条件的。
4.HashMap死循环分析

HashMap 致使 CPU 100% 的缘由就是由于 HashMap 死循环致使的,那 HashMap 是如何形成死循环的?接下来咱们一块儿来看。
以 JDK 1.7 为例,假设 HashMap 的默认大小为 2,HashMap 自己中有一个键值 key(5),咱们再使用两个线程:t1 添加 key(3),t2 添加 key(7),首先两个线程先把 key(3) 和 key(7) 都添加到 HashMap 中,此时由于 HashMap 的长度不够用了就会进行扩容操做,而后这时线程 t1 在执行到 Entry<K,V> next = e.next; 时,交出了 CPU 的使用权,源代码以下:

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;
}
}
}安全

那么此时线程 t1 中的 e 指向了 key(3),而 next 指向了 key(7) ;以后线程 t2 从新 rehash 以后链表的顺序被反转,链表的位置变成了 key(5) -> key(7) -> key(3),其中 “->” 用来表示下一个元素,当 t1 从新得到执行权以后,先执行 newTalbe[i] = e 把 key(3) 的 next 设置为 key(7),而下次循环时查询到 key(7) 的 e.next 为 key(3),因而就形 成了 key(3) 和 key(7) 的环形引用,就致使了死循环的产生,以下图所示:

![](https://s4.51cto.com/images/blog/202007/31/cce05aec85fda2d0a701473f3654477e.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

HashMap 发生死循环的一个重要缘由是 JDK 1.7 时链表的插入是首部倒序插入的,而 JDK 1.8 时已经变成了尾部插入,有人把这个死循环的问题反馈给了 Sun 公司,但它们认为这不是一个问题,由于 HashMap 自己就是非线程安全的,若是要在多线程使用建议使用 ConcurrentHashMap 替代 HashMap,但面试中这个问题被问的频率比较高,因此在这里就特殊说明一下。
小结

HashMap 是非线程安全的,以 JDK 1.7 为例,当多线程并发扩容时就会出现环形引用的问题,从而致使死循环的出现,一直死循环就会致使 CPU 运行 100%,因此在多线程使用时,咱们须要使用 ConcurrentHashMap 来替代 HashMap,但只有懂得其中的因果关系才能吊打面试官,好了,本节内容到这里就结束了,咱们下期再见。

上期中奖名单:皮卡皮卡、一步、好好学习、谈笑、包子有话要讲。
以上中奖的朋友,请加个人微信:GG_Stone 领取奖励。
【END】
近期热文

* 面试突击 005 | Redis 是如何实现高可用的?它的实现方式有哪些?
* 面试突击 004 | 如何排查 Redis 中的慢查询?视频实战篇
* 面试突击 003 | Redis 如何实现查询附近的人?
* 面试突击 002 | Redis 是如何处理已过时元素的?
* 面试突击 001 | Redis 如何从海量数据中查询出某一个 Key?
* Java面试详解(2020版):500+ 面试题和核心知识点详解
关注下方二维码,订阅更多精彩内容
![](https://s4.51cto.com/images/blog/202007/31/dba489b49a779bc90fe04f2d66b60f44.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
相关文章
相关标签/搜索