HashMap在JDK1.7下死链问题(源码+图示)

在JDK7时,因为是头插法,在多线程并发扩容下,扩容的时候就有可能出现环形链表,造成死循环。

比如原先在桶下标1位置:

此时,有两个线程来扩容,调用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);
    }

注意,这里两个线程都创建了newTable传入transfer()方法,所以,两个线程的新数组是独立的。

线程A先执行,到第10行(e.next = newTable[i]),还没执行第10行,时间片用完了,轮到线程B。

这时,线程A的e = ③,next = ⑦

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

线程B咔哧咔哧把扩容流程走完了,此时桶下标为1的链表为下图

节点的状态被修改了,⑦->③,③->null

回到线程A执行,线程A的newTable还是空的,它要把节点搬过来了,从transfer第10行,e.next = newTable[i];开始执行,它的e为③,next为⑦

第一轮循环:

执行e.next = newTable[i];newTable[i] = e;后

执行e = next; (这里的next还是之前A线程记录的,是⑦)

这时e = ⑦

第二轮循环:

执行Entry<K,V> next = e.next; 因为线程B已经改了节点指向,⑦->③,所以next = ③

执行e.next = newTable[i]; newTable[i] = e;

执行e = next; 这时e为③

第三轮循环:

执行Entry<K,V> next = e.next;

next为null

执行e.next = newTable[i]; newTable[i] = e;

③就到头部去了,③->⑦,⑦->③,虽然最后执行e = next; e就为null,退出了循环,但死链已成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值