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


    目录

    问题描述

    原因剖析

    JDK1.7HashMap扩容过程

    源码

     图解

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

    总结


    问题描述

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

    原因剖析

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

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

    JDK1.7HashMap扩容过程

    源码

    1. //扩容后将数据填充进新桶
    2. void transfer(Entry[] newTable, boolean rehash) {
    3. int newCapacity = newTable.length;//新的容量
    4. for (Entry e : table) {
    5. while(null != e) {
    6. Entry next = e.next;
    7. if (rehash) {
    8. e.hash = null == e.key ? 0 : hash(e.key);
    9. }
    10. int i = indexFor(e.hash, newCapacity);//定位Hash桶,计算的结果可能为:初始位置 或者 初始位置 + 扩容量
    11. e.next = newTable[i];//扩容插入也会采用头插法,与旧的hash桶相比链表的顺序可能会被颠倒,并且分散到 初始位置 和 初始位置 + 扩容量两个位置中
    12. newTable[i] = e;
    13. e = next;
    14. }
    15. }
    16. }

     图解

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

  • 相关阅读:
    基于verdaccio工具搭建npm私服vue组件库
    《论文阅读》Controllable Abstractive Dialogue Summarization with Sketch Supervision
    解决ios向mac复制文字不成功的一种方法
    HQL 55 题【持续更新】
    数模国赛:四川赛区即将线上答辩
    【vue】浏览器播放提示音audio
    大模型产品化第一年​:战术、运营与战略
    手把手教程-自动抢单某知名外卖程序制作
    Python武器库开发-基础篇(四)
    SpringBoot整合数据库连接
  • 原文地址:https://blog.csdn.net/qq_57031340/article/details/126825488