hashMap扩容时应该注意这些死循环问题
hashMap死循环
1.原因: jdk1.7时使用头插入法 ,1.8之后改成了尾插入法解决了这个问题
HashMap死循环问题图解
在HashMap的数组真实长度达到阈值后,会调用扩容方法:
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);
}
这里可以看到如果有两个线程A和B,那么在调用transfer方法之前会在各自的线程中创建新的数组,然后进入到transfer方法中将节点转移,再看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; ------(1)
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),待会会用到,首先假设HashMap中的结构是这样的:
那么线程A如果执行到(1)的位置,那么e为节点5,next为节点6,这个时候线程B开始运行,在自己的扩容数组里面运行:
e.next = newTable[i]; newTable[i] = e;
这个时候结构图:
然后e=next;
在进入循环执行:
e.next = newTable[i]; newTable[i] = e;
这个时候结构为:

然后线程B执行完毕。线程A开始从(1)后面继续执行,这个时候也是先执行
e.next = newTable[i]; newTable[i] = e;
然后e=next;这个时候e是节点6,然后再进入循环,执行上面两行程序后的结构如下:

但是这个时候由于6的next是有值的,是节点5,所以再执行e=next;的时候,e不为空,还会进入一次循环,在执行将节点插入头部的操作,所以这个时候的结构图:

可以,看到已经成为了环状链表,当执行get操作的时候就会产生死循环。
到此这篇关于hashMap扩容时应该注意这些死循环问题的文章就介绍到这了,更多相关hashMap扩容内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Dependency ‘XXX:‘ not found问题的三步解决
这篇文章主要介绍了Dependency ‘XXX:‘ not found问题的三步解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-01-01
SpringBoot Redis配置Fastjson进行序列化和反序列化实现
这篇文章主要介绍了SpringBoot Redis配置Fastjson进行序列化和反序列化实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2019-10-10
Java实现正则匹配 “1234567” 这个字符串出现四次或四次以上
文章介绍了如何在Java中使用正则表达式匹配一个字符串四次或四次以上的出现,首先创建正则表达式,然后使用Pattern和Matcher类进行匹配和计数,通过示例代码展示了如何实现这一功能,并解释了匹配的整体次数和精确出现次数的逻辑,感兴趣的朋友一起看看吧2025-02-02


最新评论