HashMap学习:1.7 迁移死循环分析(通俗易懂)

前言

JDK1.7由于采用的头插法,所以多线程情况下可能会产生死循环问题。

正文

头插法

在这里插入图片描述

就是每次从旧容器中的hash桶中取出数据后,放到新容器的头节点问题,如果此时头结点位置为空,直接放置即可,如果不为空将头节点的数据往后挪一位,让出位置给迁移节点。

容器迁移

void resize(int newCapacity) {
    
    
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
    
    
            threshold = Integer.MAX_VALUE;
            return;
        }
 
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

从以上代码可知,有两个线程的情况下会创建两个新的容器,当迁移完成后将新的容器覆盖到旧的上面;

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);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

迁移主要的思路是,遍历当前hash桶数组,从每个桶的头节点遍历,挨个迁移到新的组数的头节点,并且原来新节点的头节点往后移动;以上代码在多线程下可能产生死循环问题,下面分析下为什么会产生。

死循环分析

1、假设现在需要迁移的旧集合结构如下:

在这里插入图片描述

2、假设现在有两个线程1和线程2,由于他们都会创建新的容器,所以此时的结构如下:

在这里插入图片描述

3、假设线程2执行到此代码片段后被挂起了,恰好e=A,那next=B了,我们记住线程2的状态哈

在这里插入图片描述

4、线程1先获得CPU的执行权,当执行完成后的结构如下:

在这里插入图片描述

5、由步骤4可知,此时的A.next=null,B.next=A,此时的状态是正常的。线程2开始获取CPU的执行时间,由于每个线程都创建了一个新的容器,它此时的状态为步骤2中图片所示,线程2开始工作后,从步骤3中的代码段开始执行,由于代码段中保存了e=Anext=B,所以会先进行A的迁移,A迁移完成后由于代码中保存了next,所以还会对B进行迁移。

在这里插入图片描述

6、步骤5中还未形成死循环,由于线程1中迁移完成造成,B.next=A,所以代码块会判断B.next是否为null,不为null则拿出下个值进行迁移,由于next=A,所以又将A迁移到新的容量中去了。此时A.next=B,此时就真正形成了闭环。又对A进行判断是否有next…不断添加

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45031612/article/details/131274724
今日推荐