目录
在JDK1.7版本下,HashMap在多线程并发下会出现死循环
首先在jdk1.7中,HashMap的底层是使用数组+链表实现的,而链表使用的是头插法插入数据
多线程并发时,在扩容的时候,新的数组中的某个位置链表,使用头插法插入数据的过程中,指针指向可能会出现问题,造成循环链表
- //扩容后将数据填充进新桶
- void transfer(Entry[] newTable, boolean rehash) {
- int newCapacity = newTable.length;//新的容量
- for (Entry
e : table) { - while(null != e) {
- Entry
next = e.next; - if (rehash) {
- e.hash = null == e.key ? 0 : hash(e.key);
- }
- int i = indexFor(e.hash, newCapacity);//定位Hash桶,计算的结果可能为:初始位置 或者 初始位置 + 扩容量
- e.next = newTable[i];//扩容插入也会采用头插法,与旧的hash桶相比链表的顺序可能会被颠倒,并且分散到 初始位置 和 初始位置 + 扩容量两个位置中
- newTable[i] = e;
- e = next;
- }
- }
- }






同时有个多个线程触发了扩容方法,但是线程1在执行完Entry

线程1这时重新获得cpu资源,继续运行,线程2已扩容完毕,这时线程1扩容的就是扩容后的数组了,但是e和next的指向在线程冻结前存储过了,于是有

继续运行,e.next指向新数组中对应的位置

将e结点放置在新数组

e指向next

第一次循环结束e不指向null,循环继续
这时候e.next指向新数组,其实是不变的

将e结点放置到新数组

e指向next

e不指向空,继续循环

这时候,执行e.next = newTable[i],a又指向了b,这时候头插出现了循环,导致死锁问题

就是JDK1.7的头插法造成了循环问题,因此在JDK1.8中对HashMap的扩容方法进行了改进,从头插法改成了尾插法