HashMap在JDK1.7中多线程并发会出现死循环,超详细图解

目录

问题描述

原因剖析

JDK1.7HashMap扩容过程

源码

 图解

多线程并发下,扩容出现循环的图解

总结


问题描述

在JDK1.7版本下,HashMap在多线程并发下会出现死循环

原因剖析

首先在jdk1.7中,HashMap的底层是使用数组+链表实现的,而链表使用的是头插法插入数据

多线程并发时,在扩容的时候,新的数组中的某个位置链表,使用头插法插入数据的过程中,指针指向可能会出现问题,造成循环链表

JDK1.7HashMap扩容过程

源码

//扩容后将数据填充进新桶
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);//定位Hash桶,计算的结果可能为:初始位置 或者 初始位置 + 扩容量
            e.next = newTable[i];//扩容插入也会采用头插法,与旧的hash桶相比链表的顺序可能会被颠倒,并且分散到 初始位置 和 初始位置 + 扩容量两个位置中
            newTable[i] = e;  
            e = next;  
        }  
    }  
}

 图解

  1. 假设现在hashmap数组的初始长度为3,有一个键值对a已经存在
  2. 依次插入数据b、c,假设b、c都和a发生哈希碰撞,即这三个键值对的hash值是一样的,那么这个位置存储的就是一个链表,使用头插法插入结点,即在链表头部插入新数据
  3. 单线程扩容
    1. 开辟新数组,让next指向e结点的下一个结点:int newCapacity = newTable.length;
    2. 获取有数据的位置的旧数组下标,并通过该数据计算新数组下标,即在新数组中的位置:int i = indexFor(e.hash, newCapacity);
    3. 让头结点指向新数组中的位置:e.next = newTable[i],这里假设这三个数据在新数组中还是会发生哈希碰撞
    4. 将e结点赋给新数组对应位置:newTable[i] = e; ​​​​​​​
    5. 让e指向next​​​​​​​e = next;
    6. e结点不为空:while(null != e) { /**循环体**/ },继续循环
    7. 最终结果(因为使用头插法,所以扩容后链表会反过来):​​​​​​​

多线程并发下,扩容出现循环的图解

同时有个多个线程触发了扩容方法,但是线程1在执行完Entry<K,V> next = e.next后由于某些不可抗因素冻结线程,此时Thread获取到CPU资源,开始扩容,线程2正常执行

 线程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的扩容方法进行了改进,从头插法改成了尾插法

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值