性能ConcurrentHashmap vs HashMap

Mau*_*uli 63 java collections hashmap

如何将ConcurrentHashMap的性能与HashMap进行比较,特别是.get()操作(我特别感兴趣的是只有少数几个项目的情况,范围可能在0-5000之间)?

有没有理由不使用ConcurrentHashMap而不是HashMap?

(我知道不允许使用空值)

更新

只是为了澄清,显然在实际并发访问的情况下性能会受到影响,但是如何在没有并发访问的情况下比较性能呢?

Bil*_*ell 86

线程安全是一个复杂的问题.如果您想使对象线程安全,请有意识地做,并记录该选择.使用你的类的人会感谢你,如果它是简单的使用它是线程安全的,但如果一个曾经是线程安全的对象在未来的版本中变得不那样,他们会诅咒你.线程安全虽然非常好,但不仅仅是圣诞节!

现在问你的问题:

ConcurrentHashMap(至少在Sun的当前实现中)通过将底层映射划分为多个单独的桶来工作.获取元素本身不需要任何锁定,但它确实使用原子/易失性操作,这意味着存储器障碍(可能非常昂贵,并且干扰其他可能的优化).

即使JIT编译器在单线程情况下可以消除原子操作的所有开销,仍然存在确定要查看哪个桶的开销 - 不可否认这是一个相对快速的计算,但是,它仍然是不可能消除.

至于决定使用哪种实现,选择可能很简单.

如果这是一个静态字段,你几乎肯定想要使用ConcurrentHashMap,除非测试显示这是一个真正的性能杀手.您的类具有与该类实例不同的线程安全期望.

如果这是一个局部变量,那么HashMap就足够了 - 除非您知道对该对象的引用可能泄漏到另一个线程.通过编码到Map界面,如果发现问题,您可以在以后轻松更改它.

如果这是一个实例字段,并且该类尚未设计为线程安全的,则将其记录为不是线程安全的,并使用HashMap.

如果您知道此实例字段是该类不是线程安全的唯一原因,并且愿意接受有前途的线程安全所暗示的限制,那么请使用ConcurrentHashMap,除非测试显示出显着的性能影响.在这种情况下,您可以考虑允许类的用户以某种方式选择对象的线程安全版本,可能使用不同的工厂方法.

在任何一种情况下,将类记录为线程安全(或有条件地线程安全),因此使用您的类的人知道他们可以跨多个线程使用对象,编辑您的类的人知道他们必须在将来保持线程安全.

  • @Stu,一年后我找到了这篇文章,发现比尔的答案非常有帮助.无论OP是否感激到足以接受答案,我仍然感谢比尔花时间写出来.@Bill,谢谢! (5认同)
  • 有趣的是,"叔叔鲍勃的"[清洁代码](https://books.google.com.au/books?id=_i6bDeoCQzsC&lpg=PP1&dq=clean%20code&pg=PT401#v=onepage&q=concurrenthashmap&f=false)书中提到了这一点:在几乎所有情况下,ConcurrentHashMap实现都比HashMap表现更好.没有提供任何统计数据来支持这一点,但我有兴趣看到这个经过验证的...... (2认同)

Ata*_*ais 66

我真的很惊讶地发现这个话题太老了,但还没有人提供任何关于这个案例的测试.使用ScalaMeter我创建的测试add,getremove为双方HashMapConcurrentHashMap在两种情况下:

  1. 使用单线程
  2. 使用尽可能多的线程,因为我有可用的核心.请注意,因为HashMap它不是线程安全的,我只是HashMap为每个线程创建了单独的,但是使用了一个,共享ConcurrentHashMap.

代码可以在我的仓库中找到.

结果如下:

  • X轴(大小)表示写入地图的元素数量
  • Y轴(值)以毫秒为单位显示时间

添加方法 获取方法 删除方法

摘要

  • 如果要尽快操作数据,请使用所有可用线程.这似乎是显而易见的,每个线程都有完成工作的1/n.

  • 如果您选择单线程访问使用HashMap,它会更快.对于add方法来说,效率甚至高达3倍.只有get更快ConcurrentHashMap,但不多.

  • ConcurrentHashMap使用多个线程进行操作时,HashMaps对于每个线程单独操作同样有效.因此,不需要在不同的结构中对数据进行分区.

总而言之,ConcurrentHashMap当您使用单线程时,性能会更差,但添加更多线程来完成工作肯定会加快进程.

测试平台

AMD FX6100,16GB Ram
Xubuntu 16.04,Oracle JDK 8更新91,Scala 2.11.8

  • 很好的分析,但Collections.synchronizedCollection()锁定每个读/写的对象,这不是ConcurrentHashMap的工作方式.因此,我不会尝试从您的统计数据中推断出ConcurrentHashMap与HashMap(这是问题所要求的)的性能.也许创建另一个问题:"Collections.synchronizedCollection()vs TreeMap"并在那里发布你的答案? (2认同)