HashMap多线程环境下的死循环问题解释

hashMap在多线程环境下,调用put方法出现的死循环是由于扩容时候resize方法导致的链表出现循环。

 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;
            }
        }
    }

假设,现在有线程1和线程2调用put方法操作

线程1: 读取hashmap,准备扩容,此时线程2介入

 线程2:

调用resize方法执行扩容的时候,会去调用transfer方法,这个方法会将将原来的数组中的元素复制到新扩容的数组,

在这个过程中会改变链表的结构

 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;
            }
        }


--------------------------------------------------
假设A,B重新计算hash值后,还在是同一个下表位置0

先看table[0]位置的修改
1. 将table[0] 放到 newTable[0]的位置
2. 先遍历到A结点,将结点A放到newTable[0]位置,并和之前newTable[0]构成链表关系
3. 在遍历到B结点,将结点B放到newTable[0]位置,和之前的newTable[0]构成链表关系

这个过程中 A->B   会改变为  B->A,如下图

线程1,此时进入扩容

线程1调用resize方法,将结点移动到新的数组,

但是对于线程1仍然是A.next = B ,遍历结点时候还是会从A开始遍历,当遍历到结点B时候,本来应该是B.next = null,但是由于线程B的改动 B.next = A ,这样在遍历结点的过程中就构成了死循环。

参考博客:

https://github.com/Snailclimb/JavaGuide/blob/master/Java%E7%9B%B8%E5%85%B3/%E8%BF%99%E5%87%A0%E9%81%93Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E9%9D%A2%E8%AF%95%E9%A2%98%E5%87%A0%E4%B9%8E%E5%BF%85%E9%97%AE.md

猜你喜欢

转载自blog.csdn.net/Ditto_zhou/article/details/83576444