ConCurrentHashMap 知識點總結


通過分析Hashtable就知道,synchronized是針對整張Hash表的,即每次鎖住整張表讓線程獨佔,ConcurrentHashMap允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不同部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的hash table,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以併發進行。

有些方法需要跨段,比如size()和containsValue(),它們可能需要鎖定整個表而而不僅僅是某個段,這需要按順序鎖定所有段,操作完畢後,又按順序釋放所有段的鎖。這裏“按順序”是很重要的,否則極有可能出現死鎖,在ConcurrentHashMap內部,段數組是final的,並且其成員變量實際上也是final的,但是,僅僅是將數組聲明爲final的並不保證數組成員也是final的,這需要實現上的保證。這可以確保不會出現死鎖,因爲獲得鎖的順序是固定的。


 一、結構解析



對比上圖,HashTable實現鎖的方式是鎖整個hash表,而ConcurrentHashMap的實現方式是鎖桶(簡單理解就是將整個hash表想象成一大缸水,現在將這大缸裏的水分到了幾個水桶裏,hashTable每次都鎖定這個大缸,而ConcurrentHashMap則每次只鎖定其中一個 桶)。

    ConcurrentHashMap將hash表分爲16個桶(默認值),諸如get,put,remove等常用操作只鎖當前需要用到的桶。試想,原來 只能一個線程進入,現在卻能同時16個寫線程進入,併發性的提升是顯而易見的。


   ConcurrentHashMap和Hashtable主要區別就是圍繞着鎖的粒度以及如何鎖,可以簡單理解成把一個大的HashTable分解成多個,形成了鎖分離。
而Hashtable的實現方式是---鎖整個hash表


ConCurrentHashMap remove()方法    

當對ConcurrentHashMap進行remove操作時,並不是進行簡單的節點刪除操作,對比上圖,當對ConcurrentHashMap的一個segment也就是一個桶中的節點進行remove後,例如刪除節點C,C節點實際並沒有被銷燬,而是將C節點前面的反轉並拷貝到新的鏈表中,C節點後面的不需要被克隆。這樣來保持併發的讀線程不受併發的寫線程的干擾。例如現在有一個讀線程讀到了A節點,寫線程把C刪掉了,但是看上圖,讀線程仍然可以繼續讀下去;當然,如果在刪除C之前讀線程讀到的是D,那麼更不會有影響。ConcurrentHashMap中刪除一個節點並不會立刻被讀線程感受到的效果,就是傳說中的弱一致性,所以ConcurrentHashMap的迭代器是弱一致性迭代器。下面是刪除的圖:



    

那爲什麼是倒序的呢?

是因爲hashentry 結構的額不可變性,即第一次設置next屬性之後就不在改變,所以是需要克隆下來的,但是你要刪除C點你肯定是從C忘頭結點走、

ConcurrentHashMap完全允許多個讀操作併發進行,讀操作並不需要加鎖。如果使用傳統的技術,如HashMap中的實現,如果允許可以在hash鏈的中間添加或刪除元素,讀操作不加鎖將得到不一致的數據。ConcurrentHashMap實現技術是保證HashEntry幾乎是不可變的。

HashEntry代表每個hash鏈中的一個節點,其結構如下所示:

 static final class HashEntry<K,V> {  

     final K key;  
     final int hash;  
     volatile V value;  
     final HashEntry<K,V> next;  
 }  
 

可以看到除了value不是final的,其它值都是final的,這意味着不能從hash鏈的中間或尾部添加或刪除節點,因爲這需要修改next 引用值,所有的節點的修改只能從頭部開始。對於put操作,可以一律添加到Hash鏈的頭部。但是對於remove操作,可能需要從中間刪除一個節點,這就需要將要刪除節點的前面所有節點整個複製一遍,最後一個節點指向要刪除結點的下一個結點。


發佈了43 篇原創文章 · 獲贊 15 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章