JDK1.7
首先將數據分為一段一段的存儲,然后給每一段數據配一把鎖,當一個線程占用鎖訪問其中一個段數據時,其他段的數據也能被其他線程訪問。
在JDK1.7中,ConcurrentHashMap采用Segment + HashEntry的方式進行實現,結構如下:
一個 ConcurrentHashMap 里包含一個 Segment 數組。Segment 的結構和HashMap類似,是一種數組和鏈表結構,一個 Segment 包含一個 HashEntry 數組,每個 HashEntry 是一個鏈表結構的元素,每個 Segment 守護著一個HashEntry數組里的元素,當對 HashEntry 數組的數據進行修改時,必須首先獲得對應的 Segment的鎖。
1. 該類包含兩個靜態內部類 HashEntry 和 Segment ;前者用來封裝映射表的鍵值對,后者用來充當鎖的角色;
2. Segment 是一種可重入的鎖 ReentrantLock,每個 Segment 守護一個HashEntry 數組里得元素,當對 HashEntry 數組的數據進行修改時,必須首先獲得對應的 Segment 鎖。
JDK1.8
在JDK1.8中,放棄了Segment臃腫的設計,取而代之的是采用Node + CAS + Synchronized來保證并發安全進行實現,synchronized只鎖定當前鏈表或紅黑二叉樹的首節點,這樣只要hash不沖突,就不會產生并發,效率又提升N倍。
結構如下:
附加源碼,有需要的可以看看
插入元素過程(建議去看看源碼):
如果相應位置的Node還沒有初始化,則調用CAS插入相應的數據;
如果相應位置的Node不為空,且當前該節點不處于移動狀態,則對該節點加synchronized鎖,如果該節點的hash不小于0,則遍歷鏈表更新節點或插入新節點;
1. 如果該節點是TreeBin類型的節點,說明是紅黑樹結構,則通過putTreeVal方法往紅黑樹中插入節點;如果binCount不為0,說明put操作對數據產生了影響,如果當前鏈表的個數達到8個,則通過treeifyBin方法轉化為紅黑樹,如果oldVal不為空,說明是一次更新操作,沒有對元素個數產生影響,則直接返回舊值;
2. 如果插入的是一個新節點,則執行addCount()方法嘗試更新元素個數baseCount;