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 .當HashMap或LinkedHashMap,支持Collections.synchronizedMap()時,我們可以使用一個null作為鍵,並且可以使用任意數量的null值LinkedHashMap,而如果使用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.性能比較
讓我們比較ConcurrentHashMap和Collections.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.結論
在本文中,我們演示了ConcurrentHashMap和Collections.synchronizedMap()之間的區別。我們還使用簡單的JMH基準測試展示了兩者的性能。