2016-08-04 75 views
1

我们有一个应用程序,它使用了一个java.util.HashMap的实例,它通过各种间接方式共享,以便多个线程同时访问它。我们现在解决了这个问题,因为我们知道java.util.HashMap不是线程安全的,因此不应该同时访问。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方法的变化,从变化附加先调整,后调整大小先加载后

但是没有人有确切的理解,在并发访问期间究竟发生了什么?

+0

数据竞赛的定义是不可预测和随意的。对JDK的改变可能是一个完全无关的轻微改变,导致一些进程需要更长的时间,令问题更加恶化。这个问题很可能是一直存在的问题,但从几百万分之一到几千分之一。如果不重新创建问题并进行线程转储,几乎肯定不可能确定原因。线程安全性不能通过测试来证明,而只能通过仔细分析代码。 –

+0

“HashMap”中没有'getEntry()'和'addEntry()'方法。你确定它在那里,你挂了? –

+0

@OlivierGrégoire有内部。 –

回答

0

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

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

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

1

发现当调用一个hashmap.put(键,值),HashMap的阈值进行检查。如果地图大小超过此阈值,地图将被重新调整大小,并且所有条目将被重新整理。
在多线程环境中,这会使您的HashMap处于不一致的状态。至少应该保护应用程序中使用同步或锁定写入HashMap的调用。
我推荐使用java.util.concurrent.ConcurrentHashMap。