簡體   English   中英

ConcurrentHashMap 與同步 HashMap

[英]ConcurrentHashMap vs Synchronized HashMap

HashMapConcurrentHashMap上使用包裝類SynchronizedMap什么區別?

它是否只能在迭代時修改HashMap ( ConcurrentHashMap )?

同步HashMap

  1. 每個方法都使用對象級鎖進行同步。 所以synchMap 上的get 和put 方法需要一個鎖。

  2. 鎖定整個集合是一種性能開銷。 當一個線程持有鎖時,沒有其他線程可以使用該集合。

ConcurrentHashMap是在 JDK 5 中引入的。

  1. 沒有對象級別的鎖定,鎖定的粒度要細得多。 對於ConcurrentHashMap ,鎖定可能處於哈希圖存儲桶級別。

  2. 較低級別鎖定的效果是您可以擁有並發的讀取器和寫入器,這對於同步集合是不可能的。 這導致更多的可擴展性。

  3. 如果一個線程試圖修改它而另一個正在迭代它,則ConcurrentHashMap不會拋出ConcurrentModificationException

這篇文章Java 7: HashMap vs ConcurrentHashMap是一本很好的讀物。 強烈推薦。

簡短的回答:

這兩個映射都是Map接口的線程安全實現。 ConcurrentHashMap是為了在需要高並發的情況下實現更高的吞吐量而實現的。

Brian Goetz 關於ConcurrentHashMap背后的想法的文章非常好讀。 強烈推薦。

ConcurrentHashMap是線程安全的,無需同步整個地圖。 當使用鎖完成寫入時,讀取可以非常快地發生。

我們可以同時使用 ConcurrentHashMap 和 SynchronisedHashmap 來實現線程安全。 但是如果你看看他們的架構,就會有很多不同。

  1. 同步哈希圖

它將在對象級別保持鎖定。 所以如果你想執行像 put/get 這樣的任何操作,那么你必須先獲取鎖。 同時,不允許其他線程執行任何操作。 因此,一次只有一個線程可以對此進行操作。 所以這里的等待時間會增加。 可以說和 ConcurrentHashMap 相比,性能比較低。

  1. 並發哈希映射

它將在段級別保持鎖定。 它有 16 個段,並且默認保持並發級別為 16。 所以一次可以有 16 個線程可以操作 ConcurrentHashMap。 而且,讀操作不需要鎖。 因此,任意數量的線程都可以對其執行 get 操作。

如果thread1想在segment 2執行put操作,thread2想在segment 4執行put操作,這里是允許的。 意味着,16 個線程可以同時對 ConcurrentHashMap 執行更新(放置/刪除)操作。

這樣在這里等待的時間就會少一些。 因此性能相對優於同步哈希圖。

兩者都是HashMap的同步版本,核心功能和內部結構不同。

ConcurrentHashMap由內部段組成,從概念上可以將其視為獨立的 HashMap。 所有這些段都可以在高並發執行中由單獨的線程鎖定。 因此,多個線程可以從 ConcurrentHashMap 獲取/放置鍵值對,而不會相互阻塞/等待。 這是為了更高的吞吐量而實施的。

然而

Collections.synchronizedMap() ,我們得到 HashMap 的同步版本,它以阻塞方式訪問。 這意味着如果多個線程嘗試同時訪問 synchronizedMap,它們將被允許以同步方式一次獲取/放置一個鍵值對。

SynchronizedMapConcurrentHashMap都是線程安全類,可以在多線程應用中使用,主要區別在於如何實現線程安全。

SynchronizedMap獲取對整個 Map 實例的鎖,而ConcurrentHashMap將 Map 實例分成多個段並在這些段上進行鎖定。

在此處輸入圖片說明

在此處輸入圖片說明

ConcurrentHashMap使用稱為lock stripping的細粒度鎖定機制來允許更大程度的共享訪問。 因此,它提供了更好的並發性可伸縮性

此外,為ConcurrentHashMap返回的迭代器是弱一致的,而不是 Synchronized HashMap 使用的快速失敗技術

SynchronizedMap上的方法持有對象上的鎖,而在ConcurrentHashMap有一個“鎖條帶”的概念,其中鎖被持有在內容的桶上。 從而提高了可擴展性和性能。

並發哈希映射:

1)兩個map都是Map接口的線程安全實現。

2) ConcurrentHashMap 是為了在需要高並發的情況下實現更高的吞吐量。

3) 對象級別沒有鎖定。

同步哈希映射:

1) 每個方法都使用對象級鎖進行同步。

ConcurrentHashMap允許並發訪問數據。 整個地圖被分成幾段。

讀取操作即。 即使在段級別, get(Object key)也不同步。

但是寫操作即。 remove(Object key), get(Object key)在段級別獲取鎖。 只有整個地圖的一部分被鎖定,其他線程仍然可以從除了鎖定的一個段之外的各個段讀取值。

另一方面, SynchronizedMap在對象級別獲取鎖。 無論操作(讀/寫)如何,所有線程都應等待當前線程。

ConcurrentHashMap 與 Synchronized HashMap 的簡單性能測試 測試流程是在一個線程中調用put並在Map上的三個線程中同時調用get 正如@trshiv 所說,ConcurrentHashMap 具有更高的吞吐量和速度,其無鎖讀取操作。 結果是當操作次數超過10^7 ,ConcurrentHashMap 比 Synchronized HashMap 快2x倍。

同步HashMap

  1. 鎖定機制- 它鎖定整個地圖,因此多個線程無法同時訪問地圖。 所以,性能相對較差。

2.空鍵或值- 允許空作為鍵或值。

3.並發修改異常——同步映射返回的迭代器拋出並發修改異常

並發哈希映射

1.鎖定機制——鎖定部分,Concurrent hashmap 允許並發讀寫。 所以性能相對來說比同步地圖好

2.空鍵或值- 它不允許空作為鍵或值。 如果你使用它會拋出java.lang。 運行時出現NullPointerException

3.並發修改異常- 它不會拋出並發修改異常。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Ex_ConcurrentHashMap {

    public static void main(String[] args) {
        
        Map<String, String> map = new ConcurrentHashMap<>();
        map.put("one", "one");
        map.put("two", "two");
        map.put("three", "three");
        System.out.println("1st  map : "+map);
        String key = null;
        for(Map.Entry<String, String> itr : map.entrySet())
        {
            key = itr.getKey();
            if("three".equals(key))
            {
                map.put("FOUR", "FOUR");
            }
            System.out.println(key+" ::: "+itr.getValue());
        }
        System.out.println("2nd  map : "+map);
        //map.put("FIVE", null);//java.lang.NullPointerException
        map.put(null, "FIVE");//java.lang.NullPointerException
        System.out.println("3rd  map : "+map);
    }
}

在此處輸入圖片說明

同步 HashMap 示例

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class Ex_Synchronizedmap {

    public static void main(String[] args) {
        
        Map<String, String> map = new HashMap<>();
        
        map.put("one", "one");
        map.put("two", "two");
        map.put("three", "three");
        map.put("FOUR", null);
        map.put(null, "FIVE");
        
        System.out.println("map : "+map);
        
        Map<String, String> map1 = 
                Collections.synchronizedMap(map);
        System.out.println("map1 : "+map1);
        
        String key = null;
        for(Map.Entry<String, String> itr : map1.entrySet())
        {
            key = itr.getKey();
            if("three".equals(key))
            {
                map1.put("ABC", "ABC");
            }
            System.out.println(key+" ::: "+itr.getValue());
        }
        
        System.out.println("New Map :: "+map1);
        
        
        
        Iterator<Entry<String, String>> iterator = map1.entrySet().iterator();
        int i = 0;
        while(iterator.hasNext())
        {
            if(i == 1)
            {
                map1.put("XYZ", "XYZ");
            }
            Entry<String, String> next = iterator.next();
            System.out.println(next.getKey()+" :: "+next.getValue());
            i++;
        }
    }

}

在此處輸入圖片說明

根據java doc的

Hashtable 和 Collections.synchronizedMap(new HashMap()) 是同步的。 但是 ConcurrentHashMap 是“並發的”。

並發集合是線程安全的,但不受單個排除鎖的控制。

在 ConcurrentHashMap 的特殊情況下,它安全地允許任意數量的並發讀取以及可調整數量的並發寫入。 當您需要通過單個鎖阻止對集合的所有訪問時,“同步”類可能很有用,但代價是可擴展性較差。

在期望多個線程訪問公共集合的其他情況下,“並發”版本通常更可取。 當集合未共享或僅在持有其他鎖時才可訪問時,最好使用非同步集合。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM