Java:ConcurrentHashMap支持完全並發的讀


ConcurrentHashMap完全允許多個讀操作並發進行,讀操作並不需要加鎖。(事實上,ConcurrentHashMap支持完全並發的讀以及一定程度並發的寫。)如果使用傳統的技術,如HashMap中的實現,如果允許可以在hash鏈的中間添加或刪除元素,讀操作不加鎖將得到不一致的數據。但是ConcurrentHashMap實現技術是保證HashEntry幾乎是不可變的。HashEntry代表每個hash鏈中的一個節點,其結構如下所示: 

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. static final class HashEntry<K,V> {  
  2.         final K key;  
  3.         final int hash;  
  4.         volatile V value;  
  5.         final HashEntry<K,V> next;  
  6.   
  7.         HashEntry(K key, int hash, HashEntry<K,V> next, V value) {  
  8.             this.key = key;  
  9.             this.hash = hash;  
  10.             this.next = next;  
  11.             this.value = value;  
  12.         }  
  13.   
  14.     @SuppressWarnings("unchecked")  
  15.     static final <K,V> HashEntry<K,V>[] newArray(int i) {  
  16.         return new HashEntry[i];  
  17.     }  
  18.     }  


可以看到除了value不是final的,其它值都是final的,這意味着不能從hash鏈的中間或尾部添加或刪除節點,因為這需要修改next引用值,所有的節點的修改只能從頭部開始。對於put操作,可以一律添加到Hash鏈的頭部。但是對於remove操作,可能需要從中間刪除一個節點,這就需要將要刪除節點的前面所有節點整個復制一遍,最后一個節點指向要刪除結點的下一個結點。為了確保讀操作能夠看到最新的值,將value設置成volatile,這避免了加鎖。 remove操作要注意一個問題:如果某個讀操作在刪除時已經定位到了舊的鏈表上,那么此操作仍然將能讀到數據,只不過讀取到的是舊數據而已,這在多線程里面是沒有問題的。
HashEntry 類的 value 域被聲明為 Volatile 型,Java 的內存模型可以保證:某個寫線程對 value 域的寫入馬上可以被后續的某個讀線程“看”到。在 ConcurrentHashMap 中,不允許用 null作為鍵和值,當讀線程讀到某個 HashEntry 的 value 域的值為 null 時,便知道產生了沖突——發生了重排序現象,需要加鎖后重新讀入這個 value 值。這些特性互相配合,使得讀線程即使在不加鎖狀態下,也能正確訪問 ConcurrentHashMap。 

 

在看源碼實現時,對HashEntry 的 value 域的值可能為 null有些疑惑,網上都是說發生了重排序現象,后來仔細想想不完全正確,重排序發生在刪除操作時,這只是其中的一個原因,盡管ConcurrentHashMap不允許將value為null的值加入,但現在仍然能夠讀到一個為空的value就意味着此值對當前線程還不可見,主要因為HashEntry還沒有完全構造完成導致的,所以對添加和刪除(對鏈表的結構性修改都可能會導致value為null)。

 

轉自:http://blog.csdn.net/enjoyinwind/article/details/41148895


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM