【HashMap】HashMap多线程下的死循环问题及JDK8版本的修复
背景
想要记录一下关于jdk下的 hashmap 存在的一些问题:
1、许多同学都知道JDK下的 HashMap 是线程不安全的,但究竟是怎样个不安全法,在多线程下会出现怎样的问题?其中原因是什么?
多线程下HashMap可能会出现的问题
1、多线程put操作后,可能会导致元素丢失 2、往里面put元素的时候,可能会产生闭环的链表,get的时候会产生死循环(jdk8已经修复)
问题1,也不仅是 hashmap,在多线程下操作同个对象,也是很常见的了:
线程1 和 线程2 同时put值,并且发生hash 碰撞,同个hash槽本是链表,因为多个线程同时put给链表的第一个位置,后元素把前元素覆盖掉,导致元素丢失(值被改变)。
问题2:死循环问题
出现死循环是要满足几个条件的,多个线程同时往 map 里面 put 元素,并且因为 容量触发阈值,进行 resize() 进行扩容,在扩容过程中把 table[] 扩大两倍,
resize() 方法里面会把 旧table 里面的值迁移到新 table 里面去(并且,会把元素里面的值反序,也正是因为会把链表反序,才会出现死循环的问题,如果不反序,也就会少了个问题,JDK8在元素较多时,将链表转成树,也是解决了这个问题吧)
再贴一下代码
HashMap的rehash源代码(JDK8之前的)
// Put一个Key,Value对到Hash表中: public V put(K key, V value) { ...... //算Hash值 int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); //如果该key已被插入,则替换掉旧的value (链接操作) for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; //该key不存在,需要增加一个结点 addEntry(hash, key, value, i); return null; } // 检查容量是否超标 void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //查看当前的size是否超过了我们设定的阈值threshold,如果超过,需要resize if (size++ >= threshold) resize(2 * table.length); } // 新建一个更大尺寸的hash表,然后把数据从老的Hash表中迁移到新的Hash表中。 void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; ...... //创建一个新的Hash Table Entry[] newTable = new Entry[newCapacity]; //将Old Hash Table上的数据迁移到New Hash Table上 transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); } // 把table的元素填充到 newTable void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; //下面这段代码的意思是: // 从OldTable里摘一个元素出来,然后放到NewTable中 for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } } 从这个方法,可以看到,其实它会把链表反一下(如果有jdk7及之前的,可以跟一下就更容易看了)
关于这个问题,其实是发生在 JDK8版本之前才会的,JDK8及之后就修复了这个问题了,所以。。。知道就行吧。
但多线程下,即使不会出现死循环问题,但还是建议使用 ConcurrentHashMap 吧。
关于死循环问题,可参考: https://coolshell.cn/articles/9606.html
说得更清楚些,里面附了一些图解,有助理解。
(完毕~)
分类:
java
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2022-02-23 KafkaOffsetMonitor kafka 监控
2022-02-23 kafka 问题集合