ConcurrentHashMap

ConcurrentHashMap  
主要兩個結構:Segment[]  和 HashEntry[]
每個Segment是一個ReentrantLock
Segment結構跟HashMap差不多,成員:table,count,loadFactor,threshold,modCount
每個Segment繼承ReentrantLock,實現Serializable
定義了一般的map方法,get,put,clear,containsKey,containsValue,replace,rehash
table裏面是HashEntry,成員key,value,next,hash
 
找Segment的index,hash(object.hashCode()),index = (hash>>>sigmentShift(28))&segmentMask(16)
找HashEntry的index,index = (hash & tab.length-1)
總結:找Segment利用的是hash值的高四位,找Entry利用的是hash值的低四位,所以還有中間24位都沒有用到
 
get  不加鎖,只有在發現當hash值和key等相等,但value是null,纔會加鎖(readValueUnderLock),保證value的讀取。
因爲new出來的Entry是賦值給table裏面的,雖然table是volatile,但裏面的元素的賦值不是,所以就有可能出現引用和初始化被重排序,導致讀到的value是null的現象,所以只能加鎖避免。(跟是不是new JMM沒關係)
 
put 整個過程加鎖。遍歷entry鏈,如果key存在相同,覆蓋舊的value(hashEntry類裏只有value不是final的)
如果不存在,new HashEntry,將first設置爲它的next,將新entry設置到數組table相應的位置
 
rehash
小優化:預先遍歷一個bucket裏面的entry,找到最末尾連續的一段新索引(lastIdx)都相同的鏈表,將它們直接設置到新的數組位置
這裏不用擔心"直接"賦值會不會覆蓋數據,因爲每次都是擴充2倍,算法上是不會出現這種情況
剩餘的entry就需要一個一個創建新entry放到各自不同的數組位置
 
remove
保留要刪除的entry後面的鏈表,將該entry之前的節點重新創建一遍,從first開始遍歷,導致前面一段的順序反轉了
 
 
 
JDK7對ConcurrentHashMap進行改進
 
get  拋棄掉判斷null再加鎖方式,而是用unsafe工具的getObjectVolatile方式來讀數組裏的元素
 
put  先去tryLock(自旋),如果不能獲得鎖,執行
scanAndLockForPut
加入了自旋(tryLock)的機制,自旋一定次數(MAX_SCAN_RETRIES 默認64)後執行lock。
自旋過程中會檢查entry鏈的first是否有變化,如果有變化,重新從新的first開始遍歷,retires設置爲-1
返回HashEntry,在獲取鎖之前,可以先找到要替換或者是創建要插入的新entry
 
remove
不再重新創建要刪除節點之前的鏈表,而是直接做最簡單的pred.next=next。利用了系統底層級別的緩存特性。保證了讀線程的併發正確性
所以entry中的next從final變成了volatile
 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章