概述html
大神陈皓已经在疫苗:JAVA HASHMAP的死循环一文中详细描述了HashMap
多线程下产生死循环的缘由,我仔细研读了这篇大做,作了一些笔记,加上本身的一些理解
整理出一些信息,发出来与你们交流交流。算法
HashMap存储的数据结构shell
陈皓在Hash表数据结构这一节提到了HashMap
的数据结构以及扩容问题,关于这一点我以前写过的
HashMap的put和get方法原理和HashMap扩容已经有详细的描述了。数组
多线程rehash的时候如何形成闭环链表数据结构
rehash源代码
多线程
正常的rehash过程并发
数据准备
在size=2的HashMap
中按照顺序添加5, 7, 3这三个key,假设按照mod 2的算法来计算元素数组下标,那么key 5,7,3都会落在下标为1的数组桶中(发生hash冲突),以下图:
.net
把HashMap的size扩容为4后,rehash的过程线程
注意,发生hash冲突的5,7,3虽然都是在同一个链表中,可是每一个元素都得走rehash的过程,由于
HashMap
扩容后,这几个元素就未必都是在同一个链表中了3d
一、第一个是处理3这个key,先把key为3这个元素的next设置为空,并计算它在新数组中的下标,并存到新下标对应桶中,以下图:
二、第二个是处理7这个key,按照上面的约定,在新数组中3和7这个两个key仍是发生了hash冲突,那么按照HashMap
发生冲突的处理代码,链表的第一个元素存储的是最新插入的7,而后next指向3,以下图:
三、第三个是处理5这个key,以下图:
到这里一次正常rehash过程走完了,最后三个key的存储状况以下图:
并发下的rehash过程
当两个并发线程thread1和thread2都同时进入到transfer时,也便是,恰好thread1和thread2都要对HashMap
进行扩容,万一这个时候thread1执行下面的代码时,被线程调度器挂起了,而thread2则正常的把扩容的操做作完,以下图:
对于thread2
这个时候,thread1拥有执行权限了,则继续它的扩容操做,等thread1扩容完后就产生了一个环形链表了(注意这里省略了一些步骤,不太明白的,则能够看我以前写的HashMap的put和get方法原理和HashMap扩容)
这个时候,若是有个get请求,就有可能发生死循环,一直在链表中绕来绕去的,无法终止。
原文连接