场景
两个线程同时put,发现HashMap需要扩容。
执行代码现场
HashMap真正的resize是在transfer方法里面完成的。
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都执行了第一步,此时假设线程1此时时间片用完了,让出CPU挂起,也即此时,线程1挂起了。
此时线程2继续执行完第二步。
线程2执行完第三步:
此时假设线程2时间片用完了,让出CPU,挂起。
此时线程1拿到了CPU,开始执行。
因为之前线程1已经执行完步骤1,此时执行步骤2
也就是e.next = newTable[i];
此时会形成如下:
形成了环形链表了。
这样即时后面节点都顺利转移到了新的table中,那也会形成如下情况。
产生的原因是:
并发扩容+链表头插法