我们有一个应用程序,它使用了一个java.util.HashMap实例,该实例是通过不同的间接方向共享的,因此多个线程可以并发地访问它。我们现在已经修复了这个问题,因为我们知道java.util.HashMap不是线程安全的,不应该并发访问。
在修复之前,以及我们发现的原因之前,我们对JDK进行了升级(升级到IBM 7 SR3),在升级之后,我们在HashMap实例的get操作中偶尔遇到挂起(挂起发生在getEntry()方法中)。
出于好奇,我想知道,HashMap内部发生了什么,是什么导致了吊死。影响并发访问行为的实现有什么不同?
关于HashMap实现,IBM与Oracle和OpenJDK相同,这也与Java8版本(使用Node数据结构)不同。
我相信,java7u40 b43与b147的区别代表了随着升级而引入的变化。
我目前的假设是,挂起的原因与addEntry方法中的变化有关,从加先调整后更改到调整大小先加后。
但是,有没有人有一个确切的理解,究竟在并发访问过程中发生了什么?
发布于 2016-08-11 07:01:13
最后,我调试了我们编写的单元测试,并在线程挂起/循环时检查了HashMap的桶。实际上,当两个线程同时调整映射大小时,它们会在桶中创建一个循环。它最终形成了这样的结构:
bucket1[0].entry1.next = entry2;
bucket1[0].entry2.next = entry1;更详细的解释可以在这里找到调整HashMap大小:前方的危险
发布于 2016-08-11 07:23:58
当调用hashmap.put(键,值)时,将检查HashMap阈值。如果映射大小超过此阈值,映射将被调整大小,所有条目将被重新散列。
在多线程环境中,这将使HashMap处于不一致的状态。至少,您应该通过使用同步或锁来保护应用程序中写入HashMap的调用。
我建议使用java.util.concurrent.ConcurrentHashMap。
https://stackoverflow.com/questions/38761222
复制相似问题