2010-05-14 48 views
43

在Java中使用ConcurrentHashMap有什麼用?它有什麼好處?它是如何工作的?示例代碼也是有用的。Java中的ConcurrentHashMap?

+0

有用http://javarevisited.blogspot.in/2013/02/concurrenthashmap-in-java-example-tutorial-working.html – roottraveller 2017-07-03 06:45:46

回答

58

重點是提供線程安全的HashMap的實現。多線程可以讀取和寫入,而不會收到過時或損壞的數據。 ConcurrentHashMap提供了自己的同步,所以你不必顯式地同步對它的訪問。

ConcurrentHashMap另一個特徵是,它提供了putIfAbsent方法,這將原子如果指定的鍵不存在添加的映射。請看下面的代碼:

ConcurrentHashMap<String, Integer> myMap = new ConcurrentHashMap<String, Integer>(); 

// some stuff 

if (!myMap.contains("key")) { 
    myMap.put("key", 3); 
} 

此代碼不是線程安全的,因爲另一個線程可以爲"key"添加調用contains和調用put之間的映射。正確的實現將是:

myMap.putIfAbsent("key", 3); 
+4

我會更多地描述它爲「安全不同步」。它允許兩個線程同時在內部進行垃圾處理,並承諾之後會以一致的狀態結束。 – Affe 2010-05-14 17:42:50

+4

同步不是(外部)訂購保證。 – danben 2010-05-14 17:47:58

+4

這並不是保證請求監視器的線程將按照請求的順序接收它,但是它是一個事件 - 保證一旦一個線程獲取監視器並開始修改映射,其他人就不會看到它,直到修改爲止完成。在併發映射中,一個線程可以啓動一個put,然後在很長一段時間內不再被調度,其他線程可以執行gets,這樣就不會看到正在進行的put。 – Affe 2010-05-14 17:57:06

11

真正大的功能差異是它不拋出異常和/或結束時損壞別人,當你使用它改變它。

對於常規集合,如果另一個線程在訪問它時(通過迭代器)添加或刪除元素,則會引發異常。 ConcurrentHashMap讓他們進行更改並且不會停止您的線程。

請注意,它不會對從一個線程到另一個線程的更改的時間點可見性做出任何形式的同步保證或承諾。 (這有點像讀取提交的數據庫隔離,而不是一個同步映射,其行爲更像是一個可序列化的數據庫隔離(舊學校行鎖SQL可序列化,而不是Oracle-ish多序列化:))

最我知道的常見用法是在App Server環境中緩存不可變的派生信息,其中許多線程可能正在訪問相同的事物,並且如果兩個事件計算相同的緩存值並將其放置兩次因爲它們交錯等原因,則無關緊要。 (例如,它廣泛用於Spring WebMVC框架內部,用於保存從URL到Handler方法的映射等運行時衍生配置)。

+1

這根本不是真的。 javadoc明確指定所有操作都是線程安全的。 – danben 2010-05-14 17:43:40

+0

所有的操作都是線程安全的,但是在承諾之前沒有發生,就像使用同步映射一樣。當您查看ConcurrentHashMap時看到的內容是最近完成的操作的結果。其中一些可能比你開始試圖尋找時間晚得多。它的工作方式類似於讀取提交的數據庫隔離,而同步映射更像是可序列化的數據庫隔離。 – Affe 2010-05-14 17:46:23

+0

優秀答案 – SuprF1y 2016-12-31 04:47:01

22

ConcurrentHashMap允許併發訪問地圖。哈希表也提供對地圖的同步訪問,但整個地圖被鎖定以執行任何操作。

ConcurrentHashMap背後的邏輯是your entire table is not getting locked,但只有部分[segments]。每個段管理自己的HashTable。鎖定僅適用於更新。在檢索的情況下,它允許完全併發。

我們假設四個線程同時在容量爲32的映射上工作,該表被劃分爲四個段,每段管理一個容量哈希表。該集合默認維護一個16個分段的列表,每個分段用於保護(或鎖定)地圖的一個桶。

enter image description here

實際上,這意味着16個線程可以在單一時間修改集合。使用可選的併發級別構造函數參數可以提高此併發級別。

public ConcurrentHashMap(int initialCapacity, 
         float loadFactor, int concurrencyLevel) 

由於對方回答說,在ConcurrentHashMap中提供了新的方法putIfAbsent()它類似於把除值,如果鍵存在不會被覆蓋。

private static Map<String,String> aMap =new ConcurrentHashMap<String,String>(); 

if(!aMap.contains("key")) 
    aMap.put("key","value"); 

新方法也更快,因爲它避免了上述double traversingcontains方法必須找到該段並迭代該表以找到密鑰,並且方法put必須遍歷該桶並放置該密鑰。

+0

默認大小是32,那麼4個線程怎麼能夠創建大小爲16的段呢?不會是8 – 2017-05-25 08:39:40

2

它可用於記憶化:

import java.util.concurrent.ConcurrentHashMap; 
public static Function<Integer, Integer> fib = (n) -> { 
    Map<Integer, Integer> cache = new ConcurrentHashMap<>(); 
    if (n == 0 || n == 1) return n; 
    return cache.computeIfAbsent(n, (key) -> HelloWorld.fib.apply(n - 2) + HelloWorld.fib.apply(n - 1)); 
}; 
0

1.ConcurrentHashMap是線程安全的,其代碼可以由單個線程同時訪問。

2.ConcurrentHashMap同步或鎖定Map的某個部分。爲了優化ConcurrentHashMap的性能,Map根據併發級別分爲不同的分區。這樣我們就不需要同步整個Map對象。

3.默認併發級別爲16,因此映射分爲16個部分,每個部分由不同的鎖控制,這意味着16個線程可以操作。

4.ConcurrentHashMap不允許NULL值。因此,ConcurrentHashMap中的鍵不能爲空。