注:源图和总结来自享学课堂,自己消化之后略有补充修改
备注:基于1.7jdk进行分析
备注2:强烈建议自己跟着步骤一个图一个图手动画,理解深刻一点也快一点
简介
在多线程之下,在put操作的时候,会导致HashMap的Entry链表死循环,导致CPU利用率接近100%
单线程下HashMap的扩容
原理
扩容只要有三个方法:
AddEntry
resize
transfer
扩容总结
1、扩容2倍
2、新建数组,容量是以前的2倍
3、轮询oldtable上的值,计算在newtable中的位置,采用头插法以链表形式连接
实例
有如下的hashMap
按照代码中的方法,移动如下
第一次:
第二次:
第三次:
并发下的扩容
初始值:
线程1刚执行完Entry<K,V> next = e.next;就被阻塞了,线程2已经完成了所有的扩扩容操作,刚执行完Entry<K,V> next = e.next;,只剩下e.next = newTable[3] ; newTable[3] = e; e=next
此时线程的状态是这个样子
接下来线程1开始执行newTable[i]=e,即newTable[3]=e
执行e=next
开始新一轮循环
最开始线程2不是还有最后一点没有执行完吗e.next = newTable[3] ; newTable[3] = e; e=next,这个时候线程2完全执行完成
此时执行Entry<key,value> next = e.next;
执行e.next= newTable[3];,newTable[3] =e;
执行e=next;于是e=key(3);
开始新一轮while循环
执行e.next = newTable[i],newTable[i]=e;后,循环链表产生
产生循环链表之后,一旦线程调用get查找一个不存在的元素是(例如11,15),就会进入死循环,CPU消耗100%
总结:
HashMap的死循环是在并发扩容下发生的,因为一个线程先进行了扩容变成了倒序链表,后一个线程再扩容是,又进行了自己的散列,再次将倒序链表变成了正序链表,于是形成了一个环形,当get表中不存在的元素是,造成死循环