文章目錄
ConcurrentHashMap
//使用了unSafe方法,通過直接操作內存的方式來保證併發處理的安全性,使用的是硬件的安全機制。
/*
* 用來返回節點數組的指定位置的節點的原子操作
*/
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
/*
* cas原子操作,在指定位置設定值
*/
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
/*
* 原子操作,在指定位置設定值
*/
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
put
public V put(K key, V value) {
/*
* onlyIfAbsent
* false:這個value一定會設置
* true:只有當這個key的value爲空的時候纔會設置
*/
return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
//不允許key/value爲null,否則及時失敗
if (key == null || value == null) throw new NullPointerException();
//獲取key的hashCode
int hash = spread(key.hashCode());
//用來計算在這個節點總共有多少個元素,用來控制擴容或者轉移爲樹
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//初始化tab
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if (
//通過哈希計算出一個表中的位置因爲n是數組的長度,所以(n-1)&hash肯定不會出現數組越界
(f = tabAt(tab, i = (n - 1) & hash)) == null) {
//如果這個位置沒有元素的話,則通過cas的方式嘗試添加,注意這個時候是沒有加鎖的
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
/*
* 如果檢測到某個節點的hash值是MOVED,則表示正在進行數組擴張的數據複製階段,
* 則當前線程也會參與去複製,通過允許多線程複製的功能,一次來減少數組的複製所帶來的性能損失
*/
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
//再次取出要存儲的位置的元素,跟前面取出來的比較
if (tabAt(tab, i) == f) {
//取出來的元素的hash值大於0,當轉換爲樹之後,hash值爲-2
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
//查找到值,則覆蓋
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
//找到最後,沒找到值的,則新建對象.
if ((e = e.next) == null) {
//添加鏈表尾端
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
//對紅黑樹的處理
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
//鏈表節點其中個數達到8,則擴張數組或轉成樹
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
//計數
addCount(1L, binCount);
return null;
}
初始化
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
//sizeCtl初始值爲0,當小於0的時候表示在別的線程在初始化表或擴展表
//則暫停當前正在執行的線程對象,並執行其他線程。
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (
/*
SIZECTL:當前內存偏移量,
sc:期望值
-1:表示要替換的值
*/
U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
//指定了大小的時候就創建指定大小的Node數組,否則創建指定大小(16)的Node數組
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
//初始化後,sizeCtl長度爲數組長度的3/4
sizeCtl = sc;
}
break;
}
}
return tab;
}
擴容
/**
* Replaces all linked nodes in bin at given index unless table is
* too small, in which case resizes instead.
* 數組長度<64,則擴容一倍
* 否則轉成樹
*/
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
//擴容
tryPresize(n << 1);
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
synchronized (b) {
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
//把Node組成的鏈表,轉化爲TreeNode的鏈表,頭結點依然放在相同的位置
if ((p.prev = tl) == null)
hd = p;
else
tl.next = p;
tl = p;
}
//把TreeNode的鏈表放入容器TreeBin中,內部將單節點樹轉換成紅黑樹
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
private final void tryPresize(int size) {
//擴容大小>=最大的一半,直接設置成最大容量
int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
//返回大於輸入參數且最近的2的整數次冪的數
tableSizeFor(size + (size >>> 1) + 1);
int sc;
while ((sc = sizeCtl) >= 0) {
Node<K,V>[] tab = table; int n;
//如果數組還沒有初始化
//putAll的時候,會執行這兒
if (tab == null || (n = tab.length) == 0) {
n = (sc > c) ? sc : c;
//SIZECTL設置-1,表示正在初始化
if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
//雙重檢查
if (table == tab) {
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = nt;
//sc=3/4*n
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
}
}
//擴容後的大小<=sizeCtl或者當前數組長度>容量上限,則退出
else if (c <= sc || n >= MAXIMUM_CAPACITY)
break;
else if (tab == table) {
int rs = resizeStamp(n);
//表示正在擴容
if (sc < 0) {
Node<K,V>[] nt;
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
//transfer線程數+1,當前線程將加入對transfer的處理
//transfer的時候,sc表示在transfer工作的線程數
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
//沒有在初始化或擴容,則開始擴容
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
}
}
}
private static final int tableSizeFor(int c) {
/*
讓cap-1再賦值給n的目的是另找到的目標值大於或等於原值。例如二進制1000,十進制數值爲8。
如果不對它減1而直接操作,將得到答案10000,即16。顯然不是結果。
減1後二進制爲111,再進行操作則會得到原來的數值1000,即8。
*/
int n = c - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
int n = tab.length, stride;
//MIN_TRANSFER_STRIDE=16.控制線程數
//每個CPU最少處理16個長度的數組元素,可以看出核數越高步長越小,也就是說,如果一個數組的長度只有16,那只有一個線程會對其進行擴容的複製移動操作
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE; // subdivide range
//只有第一個線程進此方法的時候,纔會初始化數組.
if (nextTab == null) { // initiating
try {
@SuppressWarnings("unchecked")
//擴容一倍數組容量
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
sizeCtl = Integer.MAX_VALUE;
return;
}
//這裏標記數組初始化完成,
nextTable = nextTab;
transferIndex = n;
}
int nextn = nextTab.length;
/*
* 創建一個fwd節點,這個是用來控制併發的,當一個節點爲空或已經被轉移之後,就設置爲fwd節點
* 這是一個空的標誌節點
*/
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
//是否繼續向前查找的標誌位
boolean advance = true;
//在完成之前重新在掃描一遍數組,看看有沒完成的沒
boolean finishing = false; // to ensure sweep before committing nextTab
for (int i = 0, bound = 0;;) {
Node<K,V> f; int fh;
while (advance) {
int nextIndex, nextBound;
//i每次自減,小於範圍 或者 當前線程執行完成 則標記不需要再向前查找
if (--i >= bound || finishing)
advance = false;
//說明每個桶位置都有線程進行處理,則跳出循環
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
//每次修改transferIndex=transferIndex-stride,每個線程處理stride步長的桶
else if (U.compareAndSwapInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
//假設當前數組長度爲32,stride=16
//則nextIndex=32
//transferIndex=nextBound= nextIndex - stride=16
//bound=16
bound = nextBound;
//i=31
i = nextIndex - 1;
//設置不用向前推進
advance = false;
}
}
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
//已經完成轉移
if (finishing) {
nextTable = null;
//這裏完成nextTab=>table轉換
table = nextTab;
//爲擴容後的0.75
sizeCtl = (n << 1) - (n >>> 1);
return;
}
//正在工作的線程數-1,並返回
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
//說明執行到了最後一個線程,則直接返回
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
finishing = advance = true;
i = n; // recheck before commit
}
}
else if ((f = tabAt(tab, i)) == null)
//數組中把null的元素設置爲ForwardingNode節點(hash值爲MOVED)
advance = casTabAt(tab, i, null, fwd);
else if ((fh = f.hash) == MOVED)
//表示已有線程正在處理
advance = true; // already processed
else {
synchronized (f) {
//雙重檢查加鎖
if (tabAt(tab, i) == f) {
Node<K,V> ln, hn;
//>=0說明是node節點
if (fh >= 0) {
//爲0則表示放在擴容後數組當前索引下,否則放在n+之前位置索引下
int runBit = fh & n;
Node<K,V> lastRun = f;
/*
循環結束之後,runBit就是最後不變的hash&n的值
也就是說由lastRun節點後的hash&n的值一樣,這樣就可以直接保存,而不需要處理後面的節點
*/
for (Node<K,V> p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
//說明之後的節點都是低位
if (runBit == 0) {
ln = lastRun;
hn = null;
}
else {
//說明之後的節點都是高位
hn = lastRun;
ln = null;
}
//前面的節點不確定高低位,所以遍歷f~lastRun範圍的所有節點
//分別逆序存入ln或hn鏈表中
for (Node<K,V> p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
if ((ph & n) == 0)
ln = new Node<K,V>(ph, pk, pv, ln);
else
hn = new Node<K,V>(ph, pk, pv, hn);
}
//存入之前的位置
setTabAt(nextTab, i, ln);
//存入改變後的位置
setTabAt(nextTab, i + n, hn);
//設置fwd,這樣其他線程執行的時候,會跳過去.
setTabAt(tab, i, fwd);
advance = true;
}
else if (f instanceof TreeBin) {
//紅黑樹處理
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> lo = null, loTail = null;
TreeNode<K,V> hi = null, hiTail = null;
int lc = 0, hc = 0;
for (Node<K,V> e = t.first; e != null; e = e.next) {
int h = e.hash;
TreeNode<K,V> p = new TreeNode<K,V>
(h, e.key, e.val, null, null);
if ((h & n) == 0) {
if ((p.prev = loTail) == null)
lo = p;
else
loTail.next = p;
loTail = p;
++lc;
}
else {
if ((p.prev = hiTail) == null)
hi = p;
else
hiTail.next = p;
hiTail = p;
++hc;
}
}
/*
* 在複製完樹節點之後,判斷該節點處構成的樹還有幾個節點,
* 如果≤6個的話,就轉爲一個鏈表
*/
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
(hc != 0) ? new TreeBin<K,V>(lo) : t;
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin<K,V>(hi) : t;
//低位鏈表存儲i處
setTabAt(nextTab, i, ln);
//高位存儲i+n處
setTabAt(nextTab, i + n, hn);
//原來tab中存儲fwd,標識該桶擴容完成
setTabAt(tab, i, fwd);
advance = true;
}
}
}
}
}
}
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
if (tab != null && (f instanceof ForwardingNode) &&
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
int rs = resizeStamp(tab.length);
while (nextTab == nextTable && table == tab &&
(sc = sizeCtl) < 0) {
//如果 sc 的高 16 位也就是當前n擴容標識,不等於標識符,這說明擴容的容量變化了,不是當前原容量擴容
if ((sc >>> RESIZE_STAMP_SHIFT) != rs
// 如果 sc == 標識符 + 1
// (擴容結束了,不再有線程進行擴容)(默認第一個線程設置 sc ==rs 左移 16 位 + 2,
// 當第一個線程結束擴容了,就會將 sc 減一。這個時候,sc 就等於 rs + 1)
|| sc == rs + 1 ||
// 如果 sc == 標識符 + 65535(幫助線程數已經達到最大)
sc == rs + MAX_RESIZERS ||
//說明已經擴容完成又或者有足夠的線程擴容
transferIndex <= 0)
break;
//線程數+1,幫助一起轉換
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nextTab);
break;7
}
}
return nextTab;
}
return table;
}
get
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
//如果first匹配key-value,則直接返回
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
//遍歷查找
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
addCount
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
/*
as不爲空,說明counterCells數組已創建了,進入條件體繼續執行
若爲空,則說明數組還沒創建,預測競爭線程少,直接cas操作baseCount
如果成功,則執行下一步,若失敗,則進入條件體繼續執行
*/
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
CounterCell a; long v; int m;
//標記未發生競爭
boolean uncontended = true;
//數組爲空
if (as == null || (m = as.length - 1) < 0 ||
//給當前線程隨機生成一個數,獲取counterCells對應值,若爲null,則進入條件體
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
//條件語句執行到這裏,說明counterCells不爲空,且有值.則嘗試修改個數
//修改失敗,則標記競爭
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
//繼續累加次數
fullAddCount(x, uncontended);
return;
}
if (check <= 1)
return;
//計算CounterCell總個數
s = sumCount();
}
//擴容
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
//元素個數達到擴容的閥值,且table不爲空,且數組長度小於最大容量
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
//根據length獲取一個標識符
//高16位置0
//第16位爲1
//低15位存當前n擴容標識
int rs = resizeStamp(n);
//sc<0說明正在擴容
if (sc < 0) {
// 如果 sc 的高 16 位也就是當前n擴容標識,不等於標識符,這說明擴容的容量變化了,不是當前原容量擴容
// 如果 sc == 標識符 + 1
// (擴容結束了,不再有線程進行擴容)(默認第一個線程設置 sc ==rs 左移 16 位 + 2,
// 當第一個線程結束擴容了,就會將 sc 減一。這個時候,sc 就等於 rs + 1)
// 如果 sc == 標識符 + 65535(幫助線程數已經達到最大)
// 如果 nextTable == null(結束擴容了)
// 如果 transferIndex <= 0 (轉移狀態變化了)
// 結束循環
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
//sc+1,幫助擴容
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
//初始化擴容,
else if (U.compareAndSwapInt(this, SIZECTL, sc,
//高第16位爲1,顯示負數
//高15位容量n擴容標誌
//低16位,並行擴容線程數+1
(rs << RESIZE_STAMP_SHIFT) + 2))
//擴容,第二個參數代表新表,傳入null,表示第一次初始化新表
transfer(tab, null);
s = sumCount();
}
}
}
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
//如果當前線程隨機數爲0,則強制初始一個值
if ((h = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit(); // force initialization
h = ThreadLocalRandom.getProbe();
//設置無競爭
wasUncontended = true;
}
//標記是否發生碰撞
boolean collide = false; // True if last slot nonempty
for (;;) {
CounterCell[] as; CounterCell a; int n; long v;
//counterCells不爲null
if ((as = counterCells) != null && (n = as.length) > 0) {
//當前線程所在格子爲空
if ((a = as[(n - 1) & h]) == null) {
//鎖未佔用
if (cellsBusy == 0) { // Try to attach new Cell
//新建CounterCell
CounterCell r = new CounterCell(x); // Optimistic create
//再次檢查鎖未佔用,嘗試加鎖
if (cellsBusy == 0 &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
//表示創建CounterCell是否成功狀態
boolean created = false;
try { // Recheck under lock
CounterCell[] rs; int m, j;
//再次檢查counterCells不爲空,且格子爲佔用
if ((rs = counterCells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
//設置格子
rs[j] = r;
//設置創建狀態
created = true;
}
} finally {
//釋放鎖
cellsBusy = 0;
}
//正確創建則退出循環
if (created)
break;
continue; // Slot is now non-empty
}
}
//表明cellsBusy=1鎖上發生競爭,則重新生成隨機數,進行下次循環
collide = false;
}
//CAS 失敗,重新繼續
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
//執行到這裏說明wasUncontended=true,認爲無競爭
//且所在槽有值
//2:嘗試直接累加
else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
break;
//3:數組發生變化,說明發生了擴容或者數組長度>=cpu核心數,
//則認爲無碰撞
//當擴容超過限制後,則會不停的執行3和4,直到2成功
else if (counterCells != as || n >= NCPU)
collide = false; // At max size or stale
//4:數組沒變化且數組長度<CPU核心數,且collide認爲無碰撞,則設置有碰撞
else if (!collide)
collide = true;
//執行到這裏,說明數組沒變化,且有碰撞,則需要擴容
else if (
//無鎖
cellsBusy == 0 &&
//嘗試加鎖
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
try {
//counterCells沒發生變化
if (counterCells == as) {// Expand table unless stale
//擴容counterCells一倍容量
CounterCell[] rs = new CounterCell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
counterCells = rs;
}
} finally {
//釋放鎖
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
//重新生成隨機數,進行下次循環
h = ThreadLocalRandom.advanceProbe(h);
}
//執行到這裏說明counterCells爲空,判斷鎖是否未被佔用,嘗試加鎖
else if (cellsBusy == 0 && counterCells == as &&
U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean init = false;
try { // Initialize table
//再次判斷counterCells是否有變化
if (counterCells == as) {
//新建長度2的數組
CounterCell[] rs = new CounterCell[2];
rs[h & 1] = new CounterCell(x);
counterCells = rs;
//標記初始化完成
init = true;
}
} finally {
//釋放鎖
cellsBusy = 0;
}
//跳出循環
if (init)
break;
}
//數組爲空,搶鎖失敗,則嘗試直接累加baseCount值
else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
break; // Fall back on using base
}
}
replace#remove
public V remove(Object key) {
return replaceNode(key, null, null);
}
/**
* {@inheritDoc}
*
* @throws NullPointerException if any of the arguments are null
* key映射有值,值爲oldValue,則更新
*/
public boolean replace(K key, V oldValue, V newValue) {
if (key == null || oldValue == null || newValue == null)
throw new NullPointerException();
return replaceNode(key, newValue, oldValue) != null;
}
final V replaceNode(Object key, V value, Object cv) {
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//空判斷
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
//正在擴容,則加入擴容隊伍
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
boolean validated = false;
synchronized (f) {
//雙重檢查加鎖,DCL
if (tabAt(tab, i) == f) {
//對鏈表的處理
if (fh >= 0) {
validated = true;
for (Node<K,V> e = f, pred = null;;) {
K ek;
//匹配key
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
//匹配value
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
//修改
if (value != null)
e.val = value;
//value=null,說明執行刪除操作
else if (pred != null)
pred.next = e.next;
//當匹配的值是first節點的處理
else
setTabAt(tab, i, e.next);
}
break;
}
//臨時保存上一個節點,便於執行刪除節點操作
pred = e;
//執行最後一個節點,則退出
if ((e = e.next) == null)
break;
}
}
//紅黑樹的處理
else if (f instanceof TreeBin) {
validated = true;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(hash, key, null)) != null) {
V pv = p.val;
if (cv == null || cv == pv ||
(pv != null && cv.equals(pv))) {
oldVal = pv;
if (value != null)
p.val = value;
else if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (validated) {
if (oldVal != null) {
if (value == null)
//記數-1
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}
總結:
什麼時候擴容?
- 單節點容量>=8且容量<64,則擴容一倍
- 當數組中元素達到了sizeCtl的數量的時候,則會調用transfer方法來進行擴容
沒有實現對map進行加鎖來執行獨佔訪問,因爲採用了分段鎖,所以無法使用客戶端加鎖來創建新的原子操作,如若沒有則添加之內操作.
JDK1.8放棄分段鎖
段Segment繼承了重入鎖ReentrantLock,有了鎖的功能,每個鎖控制的是一段,當每個Segment越來越大時,鎖的粒度就變得有些大了。
分段鎖的優勢在於保證在操作不同段 map 的時候可以併發執行,操作同段 map 的時候,進行鎖的競爭和等待。這相對於直接對整個map同步synchronized是有優勢的。
缺點在於分成很多段時會比較浪費內存空間(不連續,碎片化); 操作map時競爭同一個分段鎖的概率非常小時,分段鎖反而會造成更新等操作的長時間等待; 當某個段很大時,分段鎖的性能會下降。
jdk1.8的map實現
和hashmap一樣,jdk 1.8中ConcurrentHashmap採用的底層數據結構爲數組+鏈表+紅黑樹的形式。數組可以擴容,鏈表可以轉化爲紅黑樹。
爲什麼不用ReentrantLock而用synchronized ?
減少內存開銷:如果使用ReentrantLock則需要節點繼承AQS來獲得同步支持,增加內存開銷,而1.8中只有頭節點需要進行同步。
內部優化:synchronized則是JVM直接支持的,JVM能夠在運行時作出相應的優化措施:鎖粗化、鎖消除、鎖自旋等等。
多個線程又是如何同步處理的呢?
- 同步處理主要是通過Synchronized和unsafe兩種方式來完成的。
- 在取得sizeCtl、某個位置的Node的時候,使用的都是unsafe的方法,來達到併發安全的目的
當需要在某個位置設置節點的時候,則會通過Synchronized的同步機制來鎖定該位置的節點。 - 在數組擴容的時候,則通過處理的步長和fwd節點來達到併發安全的目的,通過設置hash值爲MOVED
- 當把某個位置的節點複製到擴張後的table的時候,也通過Synchronized的同步機制來保證現程安全