在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,退出了循环,但死链已成。


被折叠的 条评论
为什么被折叠?



