JDK1.7扩容时为什么会产生并发死链问题

问题:

1、JDK1.7为啥为产生并发死链问题

  • 并发,即多线程同时访问HashMap

需要知道的一些前提知识:

1、 JDK1.7是采用有插法进行节点的添加的
2、 HashMap的扩容长度为原来的一倍

JDK1.7单线程下的扩容:

1、进行节点的转移前,需要把数组长度变为原来的一倍
2、节点(Entry)转移的源代码

void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;       //得到新数组的长度        
        for (Entry<K,V> e : table) {             //第一步:遍历整个数组对应下标下的链表,e代表一个节点
            while(null != e) {                   //当e==null时,则该链表遍历完了,继续遍历下一数组下标的链表 
                Entry<K,V> next = e.next;        //第二步:先把e节点的下一节点存起来
                if (rehash) {                    //得到新的hash值
                    e.hash = null == e.key ? 0 : hash(e.key);  
                }
                int i = indexFor(e.hash, newCapacity);      //在新数组下得到新的数组下标
                e.next = newTable[i];                       //第三步:将e的next指针指向新数组下标的位置
                newTable[i] = e;                            //第四步:将该数组下标的节点变为e节点
                e = next;                                   //第五步:遍历链表的下一节点
            }
        }
    }

3、开始演示

在这里插入图片描述

  • 看我贴出来的源码把重要的步骤分为五步
  • 1、假设已经遍历到我们3这个节点对应下标的链表了。那么此时第一步的e节点,和第二步的next节点分别指向如下图所示的节点。
    在这里插入图片描述
  • 2、执行第三步:将e的next指针指向新数组下标的位置,如图所示:

在这里插入图片描述

  • 3、执行第四步:将该数组下标的节点变为e节点,如图所示:
    在这里插入图片描述
  • 4、执行第五步:遍历链表的下一节点,如图所示:

在这里插入图片描述

  • 5、此时一次while循环已经完毕,进入下次循环,执行第一步和第二步,执行后如图所示:
    在这里插入图片描述
  • 6、按照以上步骤,我们可以将节点2和节点1都移到新的数组中,全部移到新数组后如图所示:

在这里插入图片描述
4、总结

  • JDK1.7的转移后元素的位置将反了。

JDK1.7多线程下的扩容:

1、我这里假设有两个线程分别为t1和t2,而且两个线程同时读到了需要扩容,这时候,就会导致会创建两个新的数组,而且同时要去操作这个老的数组的节点1,2,3,假设这时候两个线程都执行了第一步和第二步,这时候的状态如图所示:
在这里插入图片描述
2、假设这时候操作系统的时间片没有分到t2线程,只有t1线程在运行,那么按照单线程情况下转移的过程得到t1线程完成转移后如图所示:
在这里插入图片描述
3、接下来是t2线程运行了,那么此时移动的便是t1线程新创建的表中的节点1,2,3;现在来详细说明一下这些步骤;

  • 1)上面讲了,t2线程已经完成了源码内容的第一步和第二步的操作,接下来进行第三步:将e2的next2指针指向t2线程创建的新数组下标的位置,如图所示:
    在这里插入图片描述

  • 2)接下来执行第四步操作:将该数组下标的节点变为e2节点,如图所示:
    在这里插入图片描述

  • 接下来执行第五步操作:遍历链表的下一节点,如图所示:
    在这里插入图片描述

  • 进入新循环,再执行第一步和第二步的操作,执行后结果如图所示:
    在这里插入图片描述

  • 接下来执行第三步操作,结果如图所示:
    在这里插入图片描述

  • 接下来执行第四步操作,结果如图所示:
    在这里插入图片描述

  • 接下来执行第五步操作遍历下一节点(重点来了),如图所示:
    在这里插入图片描述

  • 再次进入下一次循环,执行第一步和第二步的操作,如图所示:
    在这里插入图片描述

  • 接下来执行第三步操作,如图所示:
    在这里插入图片描述

  • 接下来执行第四步操作,如图所示:
    在这里插入图片描述

  • 接下来也不用演示了,节点2和节点3会一直循环下去,可以导致程序一直执行,问题就很严重了,电脑直接卡死,可能还会抛出OutOfMemoryException;而且你们有没有发现,节点1给丢弃了,导致丢值的现象。

4、扩展:
虽然JDK1.8的HashMap采用尾插法避免了上述的问题,但仍旧是线程不安全的;所以如果要使用线程安全的HashMap,建议使用ConcurrentHashMap。
5、留言:

  • 有不对的地方还望大家积极留言纠错。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值