我们有一个应用程序使用java.util.HashMap
的实例 - 通过各种间接 - 共享,以便多个线程同时访问它。我们现在解决了这个问题,因为我们知道java.util.HashMap
不是线程安全的,不应该同时访问。
在修复之前,以及我们发现的原因之后,我们升级了JDK(到IBM JDK 7 SR3),在升级之后我们在该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-后
但是,是否有人确切了解并发访问期间究竟发生了什么?
答案 0 :(得分:1)
当调用 hashmap.put(key,value)时,将检查HashMap阈值。如果地图大小超过此阈值,则将调整地图的大小,并重新调整所有条目。
在多线程环境中,这将使HashMap处于不一致状态。至少,您应该保护应用程序中使用同步或锁定写入HashMap的调用
我建议使用java.util.concurrent.ConcurrentHashMap。
答案 1 :(得分:0)
我最终调试了我们编写的单元测试,并在线程挂起/循环时检查HashMap的存储区。实际上,当两个线程同时调整地图大小时,它们会在存储桶中创建一个循环。它最终得到这样的结构:
bucket1[0].entry1.next = entry2;
bucket1[0].entry2.next = entry1;
可在此处找到更详细的说明Resizing the HashMap: dangers ahead