Collections.synchronizedMap與ConcurrentHashMap

    1.概述

    在本教程中,我們將討論[Collections.synchronizedMap()](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Collections.html#synchronizedMap(java.util.Map))ConcurrentHashMap之間的區別.

    另外,我們將查看每個讀寫操作的性能輸出。

    2.差異

    Collections.synchronizedMap()ConcurrentHashMap都提供對數據集合的線程安全操作。

    Collections實用程序類使用了多態算法對集合進行操作並返回包裝後的集合。它的synchronizedMap()方法提供線程安全功能。

    顧名思義, synchronizedMap()返回一個同步的Map背靠的Map ,我們在參數提供。為了提供線程安全, synchronizedMap()允許通過返回的Map進行對備用Map所有訪問。

    ConcurrentHashMap在JDK 1.5中引入,是HashMap**的增強,它支持檢索和更新的高並發性**。 HashMap不是線程安全的,因此在線程爭用期間可能會導致錯誤的結果。

    ConcurrentHashMap類是線程安全的。因此,多個線程可以在單個對像上運行而不會帶來複雜性。

    ConcurrentHashMap,讀取操作是非阻塞的,而寫入操作則鎖定特定的段或存儲段。默認存儲桶或併發級別是16,這意味著在鎖定段或存儲桶後,任何時刻都可以寫入16個線程。

    2.1。 ConcurrentModificationException

    對於像HashMap這樣的對象,不允許執行並發操作。因此,如果我們嘗試在迭代HashMap同時對其進行更新,則將收到ConcurrentModificationException 。使用synchronizedMap()時也會發生這種情況:

    @Test(expected = ConcurrentModificationException.class)
    
     public void whenRemoveAndAddOnHashMap_thenConcurrentModificationError() {
    
     Map<Integer, String> map = new HashMap<>();
    
     map.put(1, "baeldung");
    
     map.put(2, "HashMap");
    
     Map<Integer, String> synchronizedMap = Collections.synchronizedMap(map);
    
     Iterator<Entry<Integer, String>> iterator = synchronizedMap.entrySet().iterator();
    
     while (iterator.hasNext()) {
    
     synchronizedMap.put(3, "Modification");
    
     iterator.next();
    
     }
    
     }

    但是, ConcurrentHashMap並非如此:

    Map<Integer, String> map = new ConcurrentHashMap<>();
    
     map.put(1, "baeldung");
    
     map.put(2, "HashMap");
    
    
    
     Iterator<Entry<Integer, String>> iterator = map.entrySet().iterator();
    
     while (iterator.hasNext()) {
    
     synchronizedMap.put(3, "Modification");
    
     iterator.next()
    
     }
    
    
    
     Assert.assertEquals(3, map.size());

    2.2。 null支持

    Collections.synchronizedMap()ConcurrentHashMap不同的方式處理null鍵和值

    ConcurrentHashMap不允許在鍵或值中使用null

    @Test(expected = NullPointerException.class)
    
     public void allowNullKey_In_ConcurrentHasMap() {
    
     Map<String, Integer> map = new ConcurrentHashMap<>();
    
     map.put(null, 1);
    
     }

    但是,使用Collections.synchronizedMap() ,是否支持null取決於輸入Map .HashMapLinkedHashMap,支持Collections.synchronizedMap()時,我們可以使用一個null作為鍵,並且可以使用任意數量的nullLinkedHashMap,而如果使用TreeMap ,則可以使用null值,但不能使用null鍵。

    斷言我們可以對HashMap支持的Collections.synchronizedMap()使用null鍵:

    Map<String, Integer> map = Collections
    
     .synchronizedMap(new HashMap<String, Integer>());
    
     map.put(null, 1);
    
     Assert.assertTrue(map.get(null).equals(1));

    同樣,我們可以驗證Collections.synchronizedMap()ConcurrentHashMap值中的null支持。

    3.性能比較

    讓我們比較ConcurrentHashMapCollections.synchronizedMap().的性能Collections.synchronizedMap().在這種情況下,我們使用開源框架Java Microbenchmark Harness(JMH)來比較方法的性能(以納秒為單位)

    我們對這些地圖上的隨機讀寫操作進行了比較。讓我們快速看一下我們的JMH基準代碼:

    @Benchmark
    
     public void randomReadAndWriteSynchronizedMap() {
    
     Map<String, Integer> map = Collections.synchronizedMap(new HashMap<String, Integer>());
    
     performReadAndWriteTest(map);
    
     }
    
    
    
     @Benchmark
    
     public void randomReadAndWriteConcurrentHashMap() {
    
     Map<String, Integer> map = new ConcurrentHashMap<>();
    
     performReadAndWriteTest(map);
    
     }
    
    
    
     private void performReadAndWriteTest(final Map<String, Integer> map) {
    
     for (int i = 0; i < TEST_NO_ITEMS; i++) {
    
     Integer randNumber = (int) Math.ceil(Math.random() * TEST_NO_ITEMS);
    
     map.get(String.valueOf(randNumber));
    
     map.put(String.valueOf(randNumber), randNumber);
    
     }
    
     }
    

    我們使用5個迭代和10個線程處理1,000個項目來運行性能基準。讓我們看一下基準測試結果:

    Benchmark Mode Cnt Score Error Units
    
     MapPerformanceComparison.randomReadAndWriteConcurrentHashMap avgt 100 3061555.822 ± 84058.268 ns/op
    
     MapPerformanceComparison.randomReadAndWriteSynchronizedMap avgt 100 3234465.857 ± 60884.889 ns/op
    
     MapPerformanceComparison.randomReadConcurrentHashMap avgt 100 2728614.243 ± 148477.676 ns/op
    
     MapPerformanceComparison.randomReadSynchronizedMap avgt 100 3471147.160 ± 174361.431 ns/op
    
     MapPerformanceComparison.randomWriteConcurrentHashMap avgt 100 3081447.009 ± 69533.465 ns/op
    
     MapPerformanceComparison.randomWriteSynchronizedMap avgt 100 3385768.422 ± 141412.744 ns/op

    以上結果表明**ConcurrentHashMap性能優於****Collections.synchronizedMap()**

    4.何時使用

    當數據一致性至關重要時,我們應該偏愛Collections.synchronizedMap() ;對於寫操作比讀操作多的關鍵性能應用程序,我們應該選擇ConcurrentHashMap

    5.結論

    在本文中,我們演示了ConcurrentHashMapCollections.synchronizedMap()之間的區別。我們還使用簡單的JMH基準測試展示了兩者的性能。