1、JDK1.7中ConcurrentHashMap是通過分段鎖+數組+鏈表來實現的,在ConcurrentHashMap中保存一個SegMent數組,Segment是繼承ReentrantLock的可重入鎖,也就是說對於每個Segment的操作可以通過加鎖解鎖的方式來保證線程的安全性。
2、JDK1.7ConcurrentHashMap中put數據的方式是通過hash的方式先找到插入entry在Segment數組中的位置,然后再通過Hash的方式找到entry在Segment中HashEntry中的位置,然后再執行插入到鏈表中,其中在對segment操作的時候會進行trylock如果獲取到鎖則執行插入,如果沒有獲取到鎖則會重試3次還沒獲取到則用阻塞鎖的方式獲取鎖,同時對鏈表中的節點進行刪除操作時,需要將刪除節點的前面所有節點復制一遍然后用頭插法插入鏈表(原因是每個節點的next是final的不可修改)。
3、JDK1.7ConcurrentHashMap中get數據不需要加鎖,如果讀到的數據為空則會加鎖后再讀一遍,因為可能由於某個線程在刪除某個節點導致讀到的數據為空。(刪除某個節點需要把前面所有節點復制一遍重新插入)。
4、擴容,不會對整個ConcurrentHashMap擴容只會針對某個segment擴容。
5、jdk1.8中ConcurrentHashMap的結構類似於Hashmap了也是用數組+鏈表+紅黑樹來實現的,不過它使用了CAS的方式和Synchronized加鎖來保證線程的安全。
6、jdk1.8中ConcurrentHashMap的節點put流程:
(1)、如果數據沒初始化則初始化
(2)、通過hash方式(與hashMap的hash方式類似只不過將hash值轉化為正數)找到要put節點在數組中的位置,如果該位置為空,則通過CAS的方式插入/
(3)、如果當前節點正在擴容則該線程參與擴容完成
(4)、如果該位置有節點則通過synchronized加鎖判斷是如果該節點是鏈表則查找PUT,如果是紅黑樹則執行紅黑樹的PUT,之后通過bincount判斷是否要將鏈表轉化成紅黑樹。
(5)最后更新size值並且判斷是否需要擴容。
7、ConcurrentHashMap擴容是允許多個線程並發進行擴容的,首先構造一個兩倍於當前數據長度的數組,然后計算每個線程處理的槽的空間,然后通過死循環依次遞減的方式對每個槽位進行判斷知道有實際值的槽位,通過將槽位的每個節點分成2個鏈表將高位邏輯與計算為1的鏈表插入到i+n的位置,同時將舊的節點設置為占位符然后繼續向前推進擴容操作。
8、ConcurrentHashMap的size方法是讀取baseCount和CounterCell數據的總數量,因為是並發的也不一定正確;
9、ConcurrentHashMap的弱一致性主要表現在他的一些視圖和迭代器上,通過迭代器遍歷元素的時候如果之前的元素發生修改是不會拋出fail-fast異常的,后面的元素如果修改了會體現在迭代器遍歷的結果上。