1. 程式人生 > >如何在java中使用ConcurrentHashMap

如何在java中使用ConcurrentHashMap

ConcurrentHashMap(簡稱CHM)是在Java 1.5作為Hashtable的替代選擇新引入的,是concurrent包的重要成員。在Java 1.5之前,如果想要實現一個可以在多執行緒和併發的程式中安全使用的Map,只能在HashTable和synchronized Map中選擇,因為HashMap並不是執行緒安全的。但再引入了CHM之後,我們有了更好的選擇。CHM不但是執行緒安全的,而且比HashTable和synchronizedMap的效能要好。相對於HashTable和synchronizedMap鎖住了整個Map,CHM只鎖住部分Map。CHM允許併發的讀操作,同時通過同步鎖在寫操作時保持資料完整性。我們已經在

Top 5 Java Concurrent Collections from JDK 5 and 6中學習了CHM的基礎知識,在這篇部落格中我將介紹以下幾點:

  • CHM在Java中如何實現的
  • 什麼情況下應該使用CHM
  • 在Java中使用CHM的例子
  • CHM的一些重要特性

Java中ConcurrentHashMap的實現

CHM引入了分割,並提供了HashTable支援的所有的功能。在CHM中,支援多執行緒對Map做讀操作,並且不需要任何的blocking。這得益於CHM將Map分割成了不同的部分,在執行更新操作時只鎖住一部分。根據預設的併發級別(concurrency level),Map被分割成16個部分,並且由不同的鎖控制。這意味著,同時最多可以有16個寫執行緒操作Map。試想一下,由只能一個執行緒進入變成同時可由16個寫執行緒同時進入(讀執行緒幾乎不受限制),效能的提升是顯而易見的。但由於一些更新操作,如put(),remove(),putAll(),clear()只鎖住操作的部分,所以在檢索操作不能保證返回的是最新的結果。

另一個重要點是在迭代遍歷CHM時,keySet返回的iterator是弱一致和fail-safe的,可能不會返回某些最近的改變,並且在遍歷過程中,如果已經遍歷的陣列上的內容變化了,不會丟擲ConcurrentModificationExceptoin的異常。

CHM預設的併發級別是16,但可以在建立CHM時通過建構函式改變。毫無疑問,併發級別代表著併發執行更新操作的數目,所以如果只有很少的執行緒會更新Map,那麼建議設定一個低的併發級別。另外,CHM還使用了ReentrantLock來對segments加鎖。

Java中ConcurrentHashMap putifAbsent方法的例子

很多時候我們希望在元素不存在時插入元素,我們一般會像下面那樣寫程式碼

1234567synchronized(map){if (map.get(key) == null){return map.put(key, value);} else{return map.get(key);}}

上面這段程式碼在HashMap和HashTable中是好用的,但在CHM中是有出錯的風險的。這是因為CHM在put操作時並沒有對整個Map加鎖,所以一個執行緒正在put(k,v)的時候,另一個執行緒呼叫get(k)會得到null,這就會造成一個執行緒put的值會被另一個執行緒put的值所覆蓋。當然,你可以將程式碼封裝到synchronized程式碼塊中,這樣雖然執行緒安全了,但會使你的程式碼變成了單執行緒。CHM提供的putIfAbsent(key,value)方法原子性的實現了同樣的功能,同時避免了上面的執行緒競爭的風險。

什麼時候使用ConcurrentHashMap

CHM適用於讀者數量超過寫者時,當寫者數量大於等於讀者時,CHM的效能是低於Hashtable和synchronized Map的。這是因為當鎖住了整個Map時,讀操作要等待對同一部分執行寫操作的執行緒結束。CHM適用於做cache,在程式啟動時初始化,之後可以被多個請求執行緒訪問。正如Javadoc說明的那樣,CHM是HashTable一個很好的替代,但要記住,CHM的比HashTable的同步性稍弱。

總結

現在我們知道了什麼是ConcurrentHashMap和什麼時候該用ConcurrentHashMap,下面我們來複習一下CHM的一些關鍵點。

  • CHM允許併發的讀和執行緒安全的更新操作
  • 在執行寫操作時,CHM只鎖住部分的Map
  • 併發的更新是通過內部根據併發級別將Map分割成小部分實現的
  • 高的併發級別會造成時間和空間的浪費,低的併發級別在寫執行緒多時會引起執行緒間的競爭
  • CHM的所有操作都是執行緒安全
  • CHM返回的迭代器是弱一致性,fail-safe並且不會丟擲ConcurrentModificationException異常
  • CHM不允許null的鍵值
  • 可以使用CHM代替HashTable,但要記住CHM不會鎖住整個Map

以上就是Java中CHM的實現和使用場景