2013-02-27 35 views
4

我正在經歷ConcurrentHashMapthis related tutorial,並且有一些問題。關於併發hashmap的內部工作

  1. 在這篇文章中,有人提到,ConcurrentHashMap允許多個讀者沒有任何阻擋同時讀取。這是通過將Map分成基於併發級別的不同部分以及在更新過程中僅鎖定一部分Map來實現的。缺省併發級別爲16,因此Map分爲16部分,每部分由不同的鎖定進行管理。這意味着,16個線程可以同時在Map上操作,直到它們在Map的不同部分上運行。儘管保持了線程安全性,但這使得ConcurrentHashMap具有高性能。雖然,它帶有一個警告:這樣以來put()remove()putAll()clear()更新操作不同步,併發檢索可能不反映在地圖上

  2. 另外一點,文章中也提到了最近的變化: 另一個要記住的重點是對CHM的迭代,由keySet返回的迭代器是弱一致的,它們只反映ConcurrentHashMap的狀態,並不一定反映任何最近的變化

我還沒有理解以粗體突出顯示的幾點,你能否提供更多信息或者在簡單的程序中顯示我?

+0

這給了回答您的第一個問題:http://stackoverflow.com/questions/14947723/is-concurrenthashmap-totally-safe/14947818#14947818可以 – 2013-02-27 17:27:58

回答

0

這裏真正的問題是,當多個線程與數據結構混搭時,線程不一定會在鎖定步驟中前進。

一個線程正在讀取user1。一個線程正在爲user2寫入內容。這兩個線程都不能預測其他線程將在各自進程中的位置。此外,我們無法預測用戶對這兩個流程完成的任何排序。如果寫入首先更新數據,則即使user1可能稍早請求讀取,讀取也會顯示更新的狀態。

讀取或修改迭代時以相同的方式工作,並考慮到移動到下一個(迭代時)的過程基本上成爲對Map狀態的「讀取」操作,如果不是任何內容其中的特定數據。

因此,當您允許這些數據結構的併發性時,您最終會得到一個「足夠接近」的測試時間。 (這與數據庫中的相同考慮非常相似,除了我們習慣以這種方式考慮數據庫並且時間框架是10個不同的幾個因素之外。

注意:要對由@Matts在另一個答案中...

時間線顯示了兩個線程以及每個線程的開始和結束,兩個線程的開始可以按照(a,b)或(b,a)的順序出現。結束可以以任何一種順序發生,因爲你不能說出操作需要多長時間,這給出了兩種線程可以開始和結束的4種方式(a先開始並結束,a先開始,b先結束,b開始,首先結束,b首先開始,b結束首先)現在...想象20個線程所有做同樣的事情迴應,比如說, 20個最終用戶提交這個和那個請求。它可以工作多少種可能的方式。

+0

請你也會顯示一個小程序,也會讓理解更清晰 – user2094103 2013-02-27 17:36:43

+0

不是。這個問題偶爾會出現,只有當事情發生在不同線程的同時。你不能編程,因爲它取決於處理器的速度以及運行代碼的JVM可用的CPU數量。 – 2013-02-27 19:26:08

+1

我應該指出,我過去一直試圖編寫低級別的單元測試來引發這樣的事情。在這種情況下,線程之間的不一致就是一個錯誤。我從來沒有找到一個好方法來做到這一點。給定特定的處理器配置時,有時可以使其發生一小部分時間。然後測試包括重複測試X次,並期望它在Y%的時間內失敗。一旦測試機換出另一臺......它就會退出。但即使在這一點上,它的測試更像是一次政治民意調查,這並不令人滿意。 – 2013-02-27 19:35:20

2
  1. 因爲就像放(),刪除()的putAll()或明確()更新操作不同步,併發檢索可能無法反映最新的地圖

    變化我理解它,這意味着在一個線程中對地圖的修改可能不一定會在另一個線程中同時發生檢索。請看下面的例子:

        Thread 1 starts    Thread 1's call to get("a") 
           a call to get("a")    completes, returning null 
             |         | 
    Thread 1  ---------+---------------------------------+------- 
              time -----> 
    Thread 2  -----+---------------------------+----------------- 
            |       | 
          Thread 2 starts a   Thread 2's call to 
          call to put("a", 1)   put("a", 1) completes 
    

    即使線程2 put在地圖線程1的get完成執行,線程1的值沒有「看」地圖的修改,並返回null

  2. 另一個重要的一點要記住的是遍歷所有CHM,迭代器返回由ConcurrentHashMap中的keySet的每週都是一致的,它們只是反映了一個ConcurrentHashMap的狀態,某一點而不代表任何最近的變化。

    這是一個類似的情況。如果線程1從ConcurrentHashMapkeySet獲得Iterator,並且稍後線程2在地圖中放入新條目,則線程1的Iterator不保證能看到該條目。 (可能也可能不會)。