問五:說說ConcurrentHashMap吧?

引入:如何優化Hashtable?

通過鎖細粒度化,將整個鎖拆解成多個鎖進行優化

 

這兒就引入了早期的ConcurrentHashMap

使用的是分段鎖技術,把Bucket分成幾段來存儲,爲每一段數據都配一把鎖(segment)【這樣做的原因是:爲每個Bucket都添加一把鎖的話,資源消耗大,比如1w個Bucket就要有1w個鎖

當某個程序訪問某個數據段的時候,就會獲得當前數據段(segment)的鎖,操作其子數組。而如果還有程序要訪問,就只能被阻塞,而其他未被訪問到的數據段依舊可以訪問

早期ConcurrentHashMap分配了16個segment,理論上較Hashtable會提高16倍的效率

注:ConcurrentHashMap是由Segment數組結構和HashEntry數組結構組成。

Segment是一個可重入鎖(ReentrantLock),在ConcurrentHashMap裏扮演鎖的角色;HashEntry則用於存儲鍵值對數據。

一個ConcurrentHashMap裏包含一個Segment數組。Segment的結構和HashMap類似,是一種數組和鏈表結構。一個Segment裏包含一個HashEntry數組,每個HashEntry是一個鏈表結構的元素,每個Segment守護着一個HashEntry數組裏的元素。當對HashEntry數組的數據進行修改時,必須首先獲得與它對應的segment鎖。

 

這兒就引入了現如今的ConcurrentHashMap,通過CAS和synchronized使鎖進一步細化,結構和JDK1.8的HashMap相同

附:HashMap是在package java.util包下的,但是ConcurrentHashMap是在package java.util.concurrent下的

 

concurrent分段如何實現的?具體一點

 

 

源碼分析:

裏面有很多參數和HashMap相同

也有獨特的參數,比如這個控制table大小的變量

進入到源碼中,直接看put方法,發現裏面一開始就有個對key、value判空的條件:

也就是說ConcurrentHashMap是不能夠存放空值的,而HashMap則可以。

然後下一行,能夠看到ConcurrentHashMap對hash值的確定:

由於對數組更新是使用CAS來進行更新的,所以需要不斷去做失敗重試,直到成功爲止

如果發生了hash碰撞,ConcurrentHashMap的處理邏輯:

 

ConcurrentHashMap:put方法的邏輯

1.判斷Node[]數組是否初始化,沒有就進行初始化操作

2.通過hash定位數組的索引座標,是否有Node節點,如果沒有就使用CAS進行添加(鏈表的頭結點),添加失敗則進入下一次循環

3.如果檢查到內部正在擴容,就幫助它一塊擴容

4.如果f!=null,則使用synchronized鎖住f元素(f元素就是鏈表或者紅黑二叉樹的頭元素)

    4.1 如果是Node,則執行鏈表的添加方法

    4.2 如果是TreeNode,則執行樹添加結構

5.判斷鏈表長度是否已經達到臨界值88是默認值,可以調整),當節點數超過這個值,就需要把鏈表轉化爲樹結構

 

ConcurrentHashMap的總結:比起Segment,鎖拆的更細

1.首先使用無鎖操作CAS插入頭結點,失敗則循環重試

2.若頭結點已經存在,則嘗試獲取頭結點的同步鎖,再進行操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章