今天在看《分布式java应用》这本书的时候看到作者提到HashMap在多线程并发的环境下有可能出现死循环,导致cpu100%的现象,看了下源码结合网上的分析说明下这种可能性。可能出现问题的地方是在扩容的时候
- 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);
- table = newTable;
- threshold = (int)(newCapacity * loadFactor);
- }
这个方法本身没有问题,问题出在transfer(newTable);这个方法是用来移动oldTable里的数据到newTable里。
-
-
-
- void transfer(Entry[] newTable) {
- Entry[] src = table;
- int newCapacity = newTable.length;
- 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);
- }
- }
- }
下面分析可能出现的情况,假设原来table里存放的entry链表顺序是:
oldTable[i]:a1,a2,null
线程P1运行到(1)下面这行时,e=a1(a1.next=a2),执行到(2)下面时:next=e.next=a2。这个时候切换到线程P2,线程P2执行完这个链表的循环,如果刚好a1和a2在newTable里的i值相同(int i = indexFor(e.hash, newCapacity);),那么此时的链表顺序是:
newTable[i]:a2(next=a1),a1,null
现在cpu重新切回P1,在(3)这行:e.next = newTable[i];即:a1.next=newTable[i]=a2;
然后:newTable[i]=e=a1;e = next=a2;可以看到这个时候a1.next=a2,a2.next=a1,形成回环了,这样就造成了死循环.下面是针对各个线程各变量的情况
init(初始值):a1.next=a2,a2.next=null
P1:e=a1,next=e.next=a2; waiting
P2:a2.next=a1,a1.next=null ;notify
P1:e.next=a1.next=newTable[i]=a2; newTable[i]=a1,e=next=a2
end:a1.next=a2;a2.next=a1(P2结束后产生的结果)
可以看到很偶然的情况下会出现死循环,不过一旦出现后果是非常严重的,多线程的环境还是应该用ConcurrentHashMap。
分享到:
相关推荐
详 解 hashmap 1.7 扩 容 机 制 的 数 据 迁 移 以 及 出 现 环 形 列 表 导 致 死 锁 情 况 视 频
然而,在多线程环境中使用HashMap可能会导致死循环的问题。下面我们来分析HashMap的死循环原因。 首先,HashMap的数据结构是一个数组加链表的结构。每个数组元素是一个链表的头结点,每个链表节点包含了key-value对...
计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习...
此外,引入了ConcurrentHashMap类,这是一个专门为多线程设计的高效容器,其内部使用分段锁策略,可以在并发环境下保证线程安全,避免了类似HashMap扩容引发的死锁问题。 如果你在多线程环境中使用HashMap并遇到...
首先,死循环问题通常发生在并发环境中,当多个线程同时修改HashMap时。HashMap的核心数据结构是一个数组(table[]),数组中的每个元素都是一个链表(Entry)。键值对通过哈希函数计算出其在数组中的位置,若存在...
面试中,可能会被问及HashMap的性能优化、内存占用分析、以及在特定场景下的选择,如并发环境、内存敏感的应用等。 总结,HashMap是Java编程中的基础工具,掌握其工作原理和常见面试题,不仅能帮助我们应对面试,更...
在多线程环境下,两个线程同时触发扩容可能导致循环链表的形成,从而引发死循环,这是一种严重的性能问题。 为了解决HashMap的线程不安全问题,我们可以采取以下几种策略: 1. 使用Collections.synchronizedMap()...
总的来说,马士兵老师的HashMap学习笔记不仅涵盖了HashMap的基础知识,还深入探讨了其在不同JDK版本中的实现细节,特别是并发环境下的问题和解决方案。理解这些内容对于提升Java编程技能和优化代码性能具有重要价值...
2. **数据不一致**:由于HashMap的内部实现,如resize操作,可能在并发环境下导致数据的不一致。例如,两个线程同时尝试扩容HashMap,可能会导致某些元素丢失或者被错误地放置到新的bucket中。 3. **死循环(死锁)...
在本分析中,我们将会详细探讨HashMap在不同负载因子(loadFactor)、循环次数(loop)、哈希表长度(maptablelen)和映射长度(maplen)等条件下的行为和特性。 负载因子(loadFactor)是HashMap中的一个关键参数...
Java HashMap原理分析 Java HashMap是一种基于哈希表的数据结构,它的存储原理是通过将Key-Value对存储在一个数组中,每个数组元素是一个链表,链表中的每个元素是一个Entry对象,Entry对象包含了Key、Value和指向...
HashMap导致CPU100% 的分析
HashMap 是非线程安全的,这意味着在多线程环境下,如果不采取同步措施,可能会出现数据不一致的情况。如果需要线程安全的哈希表,可以使用其线程安全的版本:ConcurrentHashMap。 总的来说,HashMap 是通过数组和...
此外,ArrayList和HashMap在并发环境下需要特别注意。如果不进行同步控制,多线程环境下可能会出现数据不一致的问题。对于并发场景,可以使用CopyOnWriteArrayList(线程安全的ArrayList变体)和ConcurrentHashMap...
这是因为在HashMap中,`hashCode()`用于确定键对象在表中的位置,而`equals()`用于在链表中找到正确的键值对。默认的`hashCode()`方法返回的是对象的内存地址,而`equals()`仅检查两个对象是否指向同一个实例。如果...
hashMap存储分析hashMap存储分析
面试中,HashMap的源码分析与实现是一个常见的考察点,因为它涉及到数据结构、并发处理和性能优化等多个核心领域。本篇文章将深入探讨HashMap的内部工作原理、关键特性以及其在实际应用中的考量因素。 HashMap基于...
- **HashMap**: 相对于`HashTable`,`HashMap`在单线程或低并发情况下具有更好的性能。这是因为`HashMap`没有同步开销,因此操作速度更快。 #### 四、内部实现细节 - **HashTable**: 内部使用了`Entry`类来表示...