最近看了jdk1.7的HashMap代码发现一段可能引发的死循环逻辑,在多线程扩容阶段,特此记录一下
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
// 遍历旧数组得到每一个key再根据新数组的长度重新计算下标存进去,如果是一个链表,则链表中的每个键值对也都要重新hash计算索引
for (Entry<K,V> e : table) {
// 如果此slot上存在元素,则进行遍历,直到e==null,退出循环
while(null != e) {
Entry<K,V> next = e.next;
// 当前元素总是直接放在数组下标的slot上,而不是放在链表的最后,所以扩容后的链表和旧的链表顺序是相反的
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
// h & (newCapacity-1);有个规律新坐标有可能是原来也可能加OldTabLength
// 把原来slot(槽)上的元素作为当前元素的下一个(即如果哈希冲突,将当前元素插入到之前元素的前面)
e.next = newTable[i];
// 新迁移过来的节点直接放置在slot(槽)位置上
newTable[i] = e;
e = next;
}
}
}
int i = indexFor(e.hash, newCapacity);需要注意下,新的元素坐标可能和原来一样或者oldTabLength+原来元素位置
循环体内流程图,1和2元素形成环形队列,当get时形成死循环CPU飙升