HashMap死循环分析的修正版

Posted 占小狼的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap死循环分析的修正版相关的知识,希望对你有一定的参考价值。

占小狼 转载请注明原创出处,谢谢!

失误

在《》一文中描述了死循环的产生过程,后来发现分析过程有点问题,由于是半夜即时写的文章,脑子有点钝,人老了,可是我才18岁啊。

之前的问题出现在误以为线程1和线程2所操作的newTable是同一个,其实是两个不同的数组,低级失误,所以需要重新分析一遍。

修正

假设HashMap初始化大小为4,插入个3节点,不巧的是,这3个节点都hash到同一个位置,如果按照默认的负载因子的话,插入第3个节点就会扩容,为了验证效果,假设负载因子是1.

 
   
   
 
  1. void transfer(Entry[] newTable, boolean rehash) {

  2.    int newCapacity = newTable.length;

  3.    for (Entry<K,V> e : table) {

  4.        while(null != e) {

  5.            Entry<K,V> next = e.next;

  6.            if (rehash) {

  7.                e.hash = null == e.key ? 0 : hash(e.key);

  8.            }

  9.            int i = indexFor(e.hash, newCapacity);

  10.            e.next = newTable[i];

  11.            newTable[i] = e;

  12.            e = next;

  13.        }

  14.    }

  15. }

以上是节点移动的相关逻辑。

插入第4个节点时,发生rehash,假设现在有两个线程同时进行,线程1和线程2,两个线程都会新建新的数组。

HashMap死循环分析的修正版

假设 线程2 在执行到 Entry<K,V>next=e.next;之后,cpu时间片用完了,这时变量e指向节点a,变量next指向节点b。

线程1继续执行,很不巧,a、b、c节点rehash之后又是在同一个位置7,开始移动节点

第一步,移动节点a

HashMap死循环分析的修正版

第二步,移动节点b

HashMap死循环分析的修正版

注意,这里的顺序是反过来的,继续移动节点c

HashMap死循环分析的修正版

这个时候 线程1 的时间片用完,内部的table还没有设置成新的newTable, 线程2 开始执行,这时内部的引用关系如下:

HashMap死循环分析的修正版

这时,在 线程2 中,变量e指向节点a,变量next指向节点b,开始执行循环体的剩余逻辑。

 
   
   
 
  1. Entry<K,V> next = e.next;

  2. int i = indexFor(e.hash, newCapacity);

  3. e.next = newTable[i];

  4. newTable[i] = e;

  5. e = next;

执行之后的引用关系如下图

HashMap死循环分析的修正版

执行后,变量e指向节点b,因为e不是null,则继续执行循环体,执行后的引用关系

HashMap死循环分析的修正版

变量e又重新指回节点a,只能继续执行循环体,这里仔细分析下: 1、执行完 Entry<K,V>next=e.next;,目前节点a没有next,所以变量next指向null; 2、 e.next=newTable[i]; 其中 newTable[i] 指向节点b,那就是把a的next指向了节点b,这样a和b就相互引用了,形成了一个环; 3、 newTable[i]=e 把节点a放到了数组i位置; 4、 e=next; 把变量e赋值为null,因为第一步中变量next就是指向null;

所以最终的引用关系是这样的:

HashMap死循环分析的修正版

节点a和b互相引用,形成了一个环,当在数组该位置get寻找对应的key时,就发生了死循环。

另外,如果线程2把newTable设置成到内部的table,节点c的数据就丢了,看来还有数据遗失的问题。

总之,千万不要在多线程写时使用HashMap,单写多读是没有问题的。

我是占小狼,对之前的错误分析,深表歉意。

每一次的思考

都想与你分享

以上是关于HashMap死循环分析的修正版的主要内容,如果未能解决你的问题,请参考以下文章

多线程下HashMap的死循环问题

多线程下HashMap的死循环问题

HashMap 死循环的探究

高并发下的HashMap

图解集合5:不正确地使用HashMap引发死循环及元素丢失

Hashmap头插法死循环