Java ConcurrentHashMap 在性能方面优于 HashMap 吗?
Posted
技术标签:
【中文标题】Java ConcurrentHashMap 在性能方面优于 HashMap 吗?【英文标题】:Java ConcurrentHashMap is better than HashMap performance wise? 【发布时间】:2011-10-05 05:47:29 【问题描述】:我刚刚在阅读《清洁代码》这本书,并遇到了这样的说法:
在 Java 还年轻的时候,Doug Lea 写了一本开创性的书[8] Concurrent 用Java编程。除了这本书,他还开发了几本 线程安全的集合,后来成为 JDK 的一部分
java.util.concurrent
包。该包中的集合是安全的 对于多线程情况,它们表现良好。 事实上,ConcurrentHashMap
实现比 HashMap 在中表现更好 几乎所有情况。它还允许同时并发 读写,它有支持普通复合的方法 否则不是线程安全的操作。如果 Java 5 是 部署环境,开始ConcurrentHashMap
请注意,在上面的引用中,我使用了“[n]”,其中 n 是某个数字,表示作者提供参考的地方,正如您所看到的,他没有为粗体部分提供任何参考。
并不是我不相信这个说法,而是我很想知道这个说法的支持证据。那么,有没有人知道任何显示ConcurrentHashMap
和HashMap
的性能统计数据的资源?或者谁能向我解释为什么 ConcurrentHashMap 比 HashMap 快?
我可能会在休息的时候研究一下 ConcurrentHashMap 在工作中的实现,但现在我想听听其他 SOers 的答案。
【问题讨论】:
如果没有看到上下文,很难说出作者的意思,但也许作者的意思是像在所有 HashMap 方法周围坚持“同步”这样的东西是一个坏主意,并且使用明确设计的东西来支持多个线程,如 ConcurrentHashMap,是一个更好的主意。 我在 Clean Code 中找不到这句话,但这是一本大书... @Matthew 在第 183 页,页面顶部。这是第 13 章 - 并发。 @Jeff Foster 不是真的,它只是说开始使用 ConcurrentHashMap 而不是 HashMap。我用确切的段落更新了我的问题。 【参考方案1】:Doug Lea 非常擅长这些事情,所以如果有一次他的 ConcurrentHashMap
表现优于 Joshua Bloch 的 HashMap
,我不会感到惊讶。然而,从 Java 7 开始,HashMap
的第一个 @author 也成为了 Doug Lea。显然,现在HashMap
没有理由比它的并发表亲慢。
出于好奇,我还是做了一些基准测试。我在 Java 7 下运行它。条目越多,性能越接近。最终ConcurrentHashMap
与HashMap
相差不到3%,这非常了不起。瓶颈实际上是内存访问,俗话说“内存是新磁盘(磁盘是新磁带)”。如果条目在缓存中,两者都会很快;如果条目不适合缓存,两者都会很慢。在实际应用程序中,地图不一定要很大才能与其他地图竞争驻留在缓存中。如果一张地图经常使用,它会被缓存;如果不是,它不会被缓存,这是真正的决定因素,而不是实现(假设两者都是由同一个专家实现的)
public static void main(String[] args)
for(int i = 0; i<100; i++)
System.out.println();
int entries = i*100*1000;
long t0=test( entries, new FakeMap() );
long t1=test( entries, new HashMap() );
long t2=test( entries, new ConcurrentHashMap() );
long diff = (t2-t1)*100/(t1-t0);
System.out.printf("entries=%,d time diff= %d%% %n", entries, diff);
static long test(int ENTRIES, Map map)
long SEED = 0;
Random random = new Random(SEED);
int RW_RATIO = 10;
long t0 = System.nanoTime();
for(int i=0; i<ENTRIES; i++)
map.put( random.nextInt(), random.nextInt() );
for(int i=0; i<RW_RATIO; i++)
random.setSeed(SEED);
for(int j=0; j<ENTRIES; j++)
map.get( random.nextInt() );
random.nextInt();
long t = System.nanoTime()-t0;
System.out.printf("%,d ns %s %n", t, map.getClass());
return t;
static class FakeMap implements Map
public Object get(Object key)
return null;
public Object put(Object key, Object value)
return null;
// etc. etc.
【讨论】:
在您的第一段中,您试图证明事情必须很快,因为 Doug Lea 管理项目?在您的代码中:并发性在哪里?? 这个故事是关于 ConcurrentHashMap 在单线程情况下比 HashMap 快,所以这就是我正在测试的。 HashMap 变慢的原因是它必须检测 ConcurrentModification 才能知道何时抛出异常。 ConcurrentHashMap 不必跟踪修改和检查 modCount。阅读您的集合类的来源。【参考方案2】:如果你只用一个线程访问 HashMap HashMap 最快(它不做任何同步),如果你从多个线程访问它 ConcurrentHashMap 比手动进行粗粒度同步更快。请参阅此处进行一些比较:
http://www.codercorp.com/blog/java/why-concurrenthashmap-is-better-than-hashtable-and-just-as-good-hashmap.html
【讨论】:
但作者似乎建议即使在单线程情况下也使用 ConcurrentHashMap。【参考方案3】:HashMap 可能较慢的原因是它必须检测 ConcurrentModification 才能知道何时抛出异常。 ConcurrentHashMap 不必检查 modCount 以知道何时抛出(但它确实将它用于 size() 和 isEmpty())。获取锁非常快,尤其是在单线程情况下,当您已经持有锁时,但检查 modCount 是两次读取和一次跳转,HashMap 必须支付抛出 CoModException 的费用。
我建议阅读你的集合类的源代码,这样你就知道在你进行方法调用时它们做了多少工作。在您有一个仅用于字典 get/put 的完全私有映射的情况下,您通常可以使用精简的 HashMap,而无需任何 modCount 甚至大小跟踪,以提高性能。
【讨论】:
【参考方案4】:这是一种很难以一种或另一种方式证明的橡胶陈述。您如何在“几乎所有情况”中衡量某事?
ConcurrentHashMap
可能比同步的HashMap
更好。争论越多,差异就越大。另一方面,不同步的HashMap
可能比ConcurrentHashMap
更快,因为后者会产生不必要的锁定开销。
我还想了解该声明的上下文,以及该书作者提出的支持该声明的证据。还有证据表明散列映射的“几乎所有”用例都涉及同步这一未陈述的假设。
【讨论】:
如果你使用 Collections.synchronizedMap( HashMap ) 你会得到一个作为一个整体被锁定的地图。 ConcurrentHashMap 确实允许一些并发访问(读和写),方法是对锁进行条带化,从而将哈希表划分为独立锁定的域。 @Alvin - 我对上下文的解读是,引用的语句实际上是指多线程应用程序。前一句是赠品。在这种情况下,我不会不同意。 @StephenC 前面的句子说多线程情况下是安全的并且它们表现良好而有争议的句子说几乎所有情况 - 所以我怀疑范围上下文只是多线程应用程序。我想我仍然同意你最初的 cmets - 证据在哪里?以上是关于Java ConcurrentHashMap 在性能方面优于 HashMap 吗?的主要内容,如果未能解决你的问题,请参考以下文章