性能文章>【译】Java Hashtable、HashMap和ConcurrentHashMap之间的性能差异>

【译】Java Hashtable、HashMap和ConcurrentHashMap之间的性能差异转载

2年前
344414

有很多文章阐明了 HashMap、HashTable 和 ConcurrentHashMap 之间的功能差异。

这篇文章通过实际示例比较了这些数据结构的性能。

如果您没有耐心阅读整篇文章,那么结论是:当你面临使用 HashMap 或 HashTable 或 ConcurrentHashMap 的决定时,您可以考虑使用 ConcurrentHashMap,因为它是线程安全的实现,不会影响性能.

性能研究

 为了研究性能特征,我整理了这个示例程序:

1 public class HashMapPerformance {
2
3   public static int ITERATION_COUNT = 10000000;
4   
5   private static AtomicInteger exitThreadCount = new AtomicInteger(0);   
6   
7   public static HashMap<Integer, Integer> myHashMap;
8   
9   public static void initData() {
10      
11      myHashMap = new HashMap<>(1000);
12      
13      for (int counter = 0; counter < 1000; ++counter) {
14         
15         myHashMap.put(counter, counter);
16      }      
17   }
18   
19   private static class Writer extends Thread{
20
21      public void run() {
22         
23         Random random = new Random();
24         
25         for (int iteration = 0; iteration < ITERATION_COUNT; ++iteration) {
26            
27            int counter = random.nextInt(1000 - 1);         
28            myHashMap.put(counter, counter);            
29         }
30         
31         exitThreadCount.incrementAndGet();
32      }
33   }
34   
35   private static class Reader extends Thread{
36
37      public void run() {
38         
39         Random random = new Random();
40         
41         for (int iteration = 0; iteration < ITERATION_COUNT; ++iteration) {
42
43            int counter = random.nextInt(1000 - 1);         
44            myHashMap.get(counter);
45         }
46         
47         exitThreadCount.incrementAndGet();
48      }
49   }      
50
51   public static void main (String args[]) throws Exception {
52      
53      initData();
54      
55      long start = System.currentTimeMillis();
56      
57      // Create 10 Writer Threads
58      for (int counter = 0; counter < 10; ++counter) {
59         
60         new Writer().start();
61      }
62      
63      // Create 10 Reader Threads
64      for (int counter = 0; counter < 10; ++counter) {
65         
66         new Reader().start();
67      }
68      
69      // Wait for all threads to complete
70      while (exitThreadCount.get() < 20) {
71         Thread.sleep(100);
72      }
73      
74      System.out.println("Total execution Time(ms): " + (System.currentTimeMillis() - start) );      
75   }   
76 }

该程序触发多个线程对“java.util.HashMap”进行并发读写。

 让我们来看看这段代码。该程序中的主要对象是第 7 行中定义的“myHashMap”。该对象的类型为“java.util.HashMap”,并在第 9 行中定义的方法“initData()”中使用 1000 条记录进行了初始化。HashMap 中的 key 和 value 都具有相同的整数值。因此,这个 HashMap 将如下图所示:

钥匙 价值
0 0
1 1
2 2
3 3
1000 1000

图:HashMap 中的数据

 'Writer' 线程在第 19 行中定义。该线程生成一个 0 到 1000 之间的随机数,并将生成的数字插入到 HashMap 中,重复 1000 万次。我们随机生成数字,以便记录可以插入到 HashMap 数据结构的不同部分。同样,第 35 行定义了一个“Reader”线程。该线程生成一个 0 到 1000 之间的随机数,并从 HashMap 中读取生成的数字。 

 你还可以注意到第 51 行中定义的 'main()' 方法。在这个方法中,将你看到创建并启动了 10 个“编写器”线程。同样,创建并启动了 10 个“阅读器”线程。然后在第 70 行,有一个代码逻辑将阻止程序终止,直到所有 Reader 和 Writer 线程完成它们的工作。 

HashMap 性能

 我们多次执行上述程序。程序平均执行时间为3.16 秒

Hashtable性能

 为了研究 Hashtable 的性能,我们将第 7 行替换为 'java.util.Hashtable' 并修改了 'Reader' 和 'Writer' 线程以从 'HashTable' 中读取和写入。然后我们多次执行该程序。该程序的平均执行时间为56.27 秒

ConcurrentHashMap 性能

 为了研究 HashTable 的性能,我们基本上用 'java.util.concurrent.ConcurrentHashMap' 替换了第 7 行,并修改了 'Reader' 和 'Writer' 线程以从 'ConcurrentHashMap' 读取和写入。然后我们多次执行该程序。该程序的平均执行时间为4.26 秒

HashMap、Hashtable、ConcurrentHashMap 性能对比

 下表总结了每个数据结构的执行时间: 

数据结构 执行时间(秒)
HashMap 3.16
ConcurrentHashMap 4.26
Hashtable 56.27

 如果你注意到 HashMap 具有最佳性能,但它不是线程安全的。它有一个可怕的问题,可能导致线程进入无限循环,最终导致  应用程序的 CPU 飙升。 

 如果你注意到 ConcurrentHashMap 的执行速度比 HashMap 稍慢,但它是 100% 线程安全的实现。 

 另一方面 Hashtable 也是线程安全的实现,但在这个测试场景中 它比 HashMap 慢 18 倍。

为什么 Hashtable 这么慢?

 Hashtable 之所以这么慢,是因为这个对象上的 'get()' 和 'put()' 方法都是同步的(如果你有兴趣,可以在这里查看 Hashtable 源代码)。当一个方法被同步时,在任何给定的时间点,只有一个线程被允许调用它。 

 在我们的示例程序中有 20 个线程。10 个线程正在调用“get()”方法,另外 10 个线程正在调用“put()”方法。在这 20 个线程中,当一个线程正在执行时,其余 19 个线程将处于 BLOCKED 状态。只有在初始线程退出 'get()'、'put()' 方法后,剩余线程才能继续前进。因此,性能将显着下降。

 为了确认这种行为,我们执行了上述程序并捕获了线程转储并使用fastThread(线程转储分析工具)对其进行了分析。工具生成了这个有趣的分析报告。以下是报告的摘录,显示了 BLOCKED 线程的传递依赖图

包含图表说明的图片已自动生成

图:Hashtable 上阻塞的线程(由fastThread生成)

报告显示有 19 个线程处于 BLOCKED 状态,而其中一个线程(即“Thread-15”)正在执行 Hashtable 中的“get()”方法。因此,只有在 'Thread-15' 退出 'get()' 方法后,其他线程才能前进并执行 'get()'、'put()' 方法。这将导致应用程序性能显着下降。

结论

 因此,如果你需要使用 map 数据结构,你可以考虑使用 ConcurrentHashMap,它提供了与 HashMap 类似的性能特征,但同时提供了类似 Hashtable 的线程安全行为。

原文地址:https://blog.fastthread.io/2022/04/22/java-hashtable-hashmap-concurrenthashmap-performance-impact/

原文作者:fastthread团队

点赞收藏
分类:标签:
willberthos

keep foolish!

请先登录,查看1条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

JDBC PreparedStatement 字段值为null导致TBase带宽飙升的案例分析

JDBC PreparedStatement 字段值为null导致TBase带宽飙升的案例分析

随机一门技术分享之Netty

随机一门技术分享之Netty

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

4
1