HashMap 在并发访问中挂起

HashMap hang in concurrent access

我们有一个使用 java.util.HashMap 实例的应用程序,该实例通过各种间接方式共享,以便多个线程同时访问它。我们现在已经解决了这个问题,因为我们知道 java.util.HashMap 不是线程安全的,不应该被并发访问。

在那次修复之前,以及我们发现的原因,我们升级了 JDK(到 IBM JDK 7 SR3)和 after在该 HashMap 实例的 get 操作期间,我们偶尔会遇到该升级挂起(挂起发生在 getEntry() 方法中。)

出于好奇,我想知道 HashMap 内部发生了什么导致挂起。影响并发访问行为的实现有何不同?

关于它出现的 HashMap 实现,IBM JDK 与 Oracle JDK 和 OpenJDK 相同,后者又不同于 Java8版本(使用 Node 数据结构)。

我相信,difference between java7u40 b43 vs b147 代表升级时引入的变化。

我目前的假设是,挂起的原因与 addEntry 方法的更改有关,从 add-first-resize-after 更改为 resize-first-add-after.

但是有没有人准确的理解,并发访问到底是怎么回事?

我最终调试了我们编写的单元测试并在线程 hangs/loops 时检查了 HashMap 的桶。事实上,当两个线程同时调整地图大小时,它们会在桶中创建一个循环。它以这样的结构结束:

bucket1[0].entry1.next = entry2;
bucket1[0].entry2.next = entry1;

可以在这里找到更详细的解释Resizing the HashMap: dangers ahead

当调用 hashmap.put(key, value) 时,将检查 HashMap 阈值。如果地图大小超过此阈值,地图将被调整大小并且所有条目将被重新散列。
在多线程环境中,这将使您的 HashMap 处于不一致状态。至少,您应该通过使用同步或锁来保护应用程序中写入 HashMap 的调用。
我建议使用 java.util.concurrent.ConcurrentHashMap。