HashMap扩容代码片段分析
Hash扩容部分的代码,由于没有同步,可能发生循环链,导致查询出现严重问题
1. 源代码
void transfer(Entry[] newTable) {
Entry[] src = table; //旧的Hash表赋给src
int newCapacity = newTable.length; //获取新的hash表的大小
//循环处理每个表元素,每个表元素是一个链式结构
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j]; //取得头节点
if (e != null) { //头节点不是空才处理
src[j] = null; //释放原表的j的位置
//循环处理链上每一个元素直至末尾,倒插法
do {
Entry<K,V> next = e.next; //缓存下一个元素
int i = indexFor(e.hash, newCapacity); //获取位置
e.next = newTable[i]; //将表元素所指向的元素设置为e的下一个
newTable[i] = e; //将e设置新表位置i所指向的元素
e = next; //将之前缓存的下一个赋给e
} while (e != null); //e为空,则退出
}
}
}
2. 多线程引起的死循环分析
将设当前情况:原来的旧表空间大小为2,由于插入新的元素,需要扩容,重新分配旧的元素至新的表空间上。
假设,当前线程一和线程二都执行到如下这一句:
src[j] = null; //释放原表的j的位置
然后线程一继续往下跑:
然后线程二继续往下跑:
总结:多线程扩容最终导致了链表循环,如果此时来hashtable上查找元素11,会导致死循环,所以hashtable是线程不安全的。