hashMap头插法产生死循环的原因

本文详细解释了在并发环境下,HashMap扩容如何导致死循环的形成过程,涉及线程T1和T2的操作顺序以及死循环的条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

死循环执行步骤1

死循环是因为并发 HashMap 扩容导致的,并发扩容的第一步,线程 T1 和线程 T2 要对 HashMap 进行扩容操作,此时 T1 和 T2 指向的是链表的头结点元素 A,而 T1 和 T2 的下一个节点,也就是 T1.next 和 T2.next 指向的是 B 节点,如下图所示:

死循环执行步骤2

死循环的第二步操作是,线程 T2 时间片用完进入休眠状态,而线程 T1 开始执行扩容操作,一直到线程 T1 扩容完成后,线程 T2 才被唤醒,扩容之后的场景如下图所示:

  从上图可知线程 T1 执行之后,因为是头插法,所以 HashMap 的顺序已经发生了改变,但线程 T2 对于发生的一切是不可知的,所以它的指向元素依然没变,如上图展示的那样,T2 指向的是 A 元素,T2.next 指向的节点是 B 元素。

 死循环执行步骤3

当线程 T1 执行完,而线程 T2 恢复执行时,死循环就建立了,如下图所示:

因为 T1 执行完扩容之后 B 节点的下一个节点是 A,而 T2 线程指向的首节点是 A,第二个节点是 B,这个顺序刚好和 T1 扩完容完之后的节点顺序是相反的。T1 执行完之后的顺序是 B 到 A,而 T2 的顺序是 A 到 B,这样 A 节点和 B 节点就形成死循环了,这就是 HashMap 死循环导致的原因。 

### 关于Java 1.7 中 HashMap 头插法导致死循环的问题 在 JDK 1.7 版本中,`HashMap` 实现存在特定条件下可能导致死循环的情况。当 `HashMap` 进行扩容操作时,如果多个线程同时修改哈希表,则可能发生结构化修改异常,进而引发死循环。 #### 原因分析 具体来说,在多线程环境中执行插入或更新操作期间,`HashMap` 使用头插法来处理链表节点的重新分配过程[^1]。这意味着新节点总是被放置在链表头部位置。然而,在并发场景下,两个或更多线程可能几乎同时尝试对同一个桶(bucket)内的元素进行重定位。这会导致某些情况下形成环形数据结构——即最后一个节点指向第一个节点,从而创建了一个闭合回路。一旦发生这种情况,遍历该链表的操作将会陷入无限循环之中[^2]。 ```java // 描述了如何通过put方法触发此问题的一个简化版伪代码片段 public V put(K key, V value) { int hash = hash(key); int i = indexFor(hash, table.length); 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++; addEntry(hash, key, value, i); // ...那么addEntry()里的逻辑会因为上述for循环无法终止而永远等待下去。 } ``` #### 解决方案 为了避免这种潜在的风险并提高程序稳定性: - **使用线程安全容器**:推荐替换为 `ConcurrentHashMap` 或其他适合高并发访问的数据结构。这些类内部实现了更复杂的同步机制以防止竞争条件的发生。 - **外部锁定策略**:虽然不太理想但从理论上讲也可以考虑在整个读写过程中对外部施加全局锁(`synchronized`)或其他形式的手动互斥控制。不过这样做通常会对性能造成较大影响,并且违背了设计初衷之一—高效性[^4]。 综上所述,对于需要支持高度并发的应用程序而言,最合理的做法还是尽快迁移到更高版本 JVM 上面去,那里已经修复了许多此类低效甚至危险的设计缺陷;而在短期内不得不继续沿用旧平台的情况下,则应优先选用专门针对并发优化过的集合实现方式作为替代品。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值