1. 程式人生 > 其它 >ConcurrentHashMap實現原理

ConcurrentHashMap實現原理

jdk1.7分段鎖:

Segment類(小型HashMap),繼承ReentrantLock

 put簡要流程:

先根據key,算出Segment陣列的下標index

加鎖

生成Entry物件,並新增到Segment的陣列或連結串列上(和HashMap相同)

segments[index].put(key,value);

釋放鎖

ConcurrentHashMap原始碼

建構函式:

建構函式中主要生成segement陣列,並將Segment物件放入segment陣列的第一個位置。

segment陣列大小為什麼沒有直接從引數獲取而是取了一個>=傳參的2^n的數?

因為計算陣列下標時,使用的是hashcode和&table.length-1(與運算) ,在之前HashMap章節有說明這個問題,這裡不再詳述。

jdk1.8 synchronized + CAS

 1.8ConcurrentHashMap採用和HashMap一樣的結構:

 1.7使用HashEntry,1.8改為Node

 1.8put簡要流程:

1、根據 key 計算出 hashcode,然後開始遍歷 table;
2、判斷是否需要初始化;
3、f 即為當前 key 定位出的 Node,如果為空表示當前位置可以寫入資料,利用 CAS 嘗試寫入,失敗則自旋保證成功。
4、如果當前位置的 hashcode == MOVED == -1,則需要進行擴容。
5、如果都不滿足,則利用 synchronized 鎖寫入資料。
6、如果數量大於 TREEIFY_THRESHOLD 則要轉換為紅黑樹。

 1 final V putVal(K key, V value, boolean onlyIfAbsent) {
 2         if (key == null || value == null) throw new NullPointerException();
 3         int hash = spread(key.hashCode());
 4         int binCount = 0;
 5         for (Node<K,V>[] tab = table;;) {//1
 6             Node<K,V> f; int
n, i, fh; 7 if (tab == null || (n = tab.length) == 0)//2 8 tab = initTable(); 9 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//3 10 if (casTabAt(tab, i, null, 11 new Node<K,V>(hash, key, value, null))) 12 break; // no lock when adding to empty bin 13 } 14 else if ((fh = f.hash) == MOVED)//4 15 tab = helpTransfer(tab, f); 16 else { 17 V oldVal = null; 18 synchronized (f) {//5 19 if (tabAt(tab, i) == f) { 20 if (fh >= 0) { 21 binCount = 1; 22 for (Node<K,V> e = f;; ++binCount) { 23 K ek; 24 if (e.hash == hash && 25 ((ek = e.key) == key || 26 (ek != null && key.equals(ek)))) { 27 oldVal = e.val; 28 if (!onlyIfAbsent) 29 e.val = value; 30 break; 31 } 32 Node<K,V> pred = e; 33 if ((e = e.next) == null) { 34 pred.next = new Node<K,V>(hash, key, 35 value, null); 36 break; 37 } 38 } 39 } 40 else if (f instanceof TreeBin) { 41 Node<K,V> p; 42 binCount = 2; 43 if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, 44 value)) != null) { 45 oldVal = p.val; 46 if (!onlyIfAbsent) 47 p.val = value; 48 } 49 } 50 } 51 } 52 if (binCount != 0) { 53 if (binCount >= TREEIFY_THRESHOLD)//6 54 treeifyBin(tab, i); 55 if (oldVal != null) 56 return oldVal; 57 break; 58 } 59 } 60 } 61 addCount(1L, binCount); 62 return null; 63 }

可以看到1.8取消了1.7中使用的ReentrantLock改為了synchronized,也看出1.8jdk對synchronized優化後的信心。