1. 程式人生 > >【JUC源碼解析】ConcurrentSkipListMap

【JUC源碼解析】ConcurrentSkipListMap

sde 空指針 順序 pri 不一致 前驅 values dex warning

簡介

基於跳表,支持並發,有序的哈希表。

跳表

紅色路徑為尋找結點F。

拿空間換時間,時間復雜度,O(nlogn).

技術分享圖片

源碼分析

內部類

Node

屬性

1         final K key; //
2         volatile Object value; //
3         volatile Node<K,V> next; // 指向下一個結點

構造方法

 1         Node(K key, Object value, Node<K,V> next) { // 構造方法
 2             this
.key = key; 3 this.value = value; 4 this.next = next; 5 } 6 7 Node(Node<K,V> next) { // 構造方法,用來構建標記結點,特點是值為自身 8 this.key = null; 9 this.value = this; 10 this.next = next; 11 }

核心方法

 1         boolean
isBaseHeader() { // 是否為頭結點(每級) 2 return value == BASE_HEADER; 3 } 4 5 boolean appendMarker(Node<K,V> f) { // 插入標記結點 6 return casNext(f, new Node<K,V>(f)); 7 } 8 9 void helpDelete(Node<K,V> b, Node<K,V> f) { //
幫助刪除 10 if (f == next && this == b.next) { // 如果b和f分別是自己的前驅結點和後繼結點 11 if (f == null || f.value != f) // 當前結點還沒有被標記刪除(後接標記結點) 12 casNext(f, new Node<K,V>(f)); // 直接刪除當前結點 13 else // 如果已經標記為刪除,則一次性刪除當前結點後標記結點 14 b.casNext(this, f.next); 15 } 16 }

Index

屬性

1         final Node<K,V> node; // 指向實際結點
2         final Index<K,V> down; // 指向下級索引
3         volatile Index<K,V> right; // 指向右側索引

構造方法

1         Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) { // 構造方法
2             this.node = node;
3             this.down = down;
4             this.right = right;
5         }

核心方法

 1         final boolean indexesDeletedNode() { // 刪除索引結點
 2             return node.value == null;
 3         }
 4 
 5         final boolean link(Index<K,V> succ, Index<K,V> newSucc) { // 鏈接(右側插入)新索引
 6             Node<K,V> n = node; // 當前索引的node域
 7             newSucc.right = succ; // 新索引的right域名設為當前索引的右側索引
 8             return n.value != null && casRight(succ, newSucc); // 若node結點沒被刪除,設置新索引到當前索引的right域
 9         }
10 
11         final boolean unlink(Index<K,V> succ) { // 解除當前索引的右側索引
12             return node.value != null && casRight(succ, succ.right); // 若node結點沒被刪除,設置右側索引的右側索引到當前索引的right域
13         }

HeadIndex

1     static final class HeadIndex<K,V> extends Index<K,V> { // 繼承Index
2         final int level; // 級別
3         HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
4             super(node, down, right);
5             this.level = level;
6         }
7     }

屬性

 1     private static final Object BASE_HEADER = new Object(); // 標明基礎層(Node層)頭節點
 2 
 3     private transient volatile HeadIndex<K,V> head; // 最頂層頭節點
 4 
 5     final Comparator<? super K> comparator; // 比較器
 6 
 7     private transient KeySet<K> keySet; // 鍵集合
 8     
 9     private transient EntrySet<K,V> entrySet; // 鍵值對集合
10     
11     private transient Values<V> values; // 值集合
12     
13     private transient ConcurrentNavigableMap<K,V> descendingMap; // 降序(鍵)集合

構造方法

 1     public ConcurrentSkipListMap() {
 2         this.comparator = null;
 3         initialize();
 4     }
 5 
 6     public ConcurrentSkipListMap(Comparator<? super K> comparator) {
 7         this.comparator = comparator;
 8         initialize();
 9     }
10 
11     public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
12         this.comparator = null;
13         initialize();
14         putAll(m); // 將m中的元素加入跳表
15     }
16 
17     public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
18         this.comparator = m.comparator();
19         initialize();
20         buildFromSorted(m); // 根據m的元素及順序構建跳表
21     }

核心方法

initialize()

1     private void initialize() {
2         keySet = null;
3         entrySet = null;
4         values = null;
5         descendingMap = null;
6         head = new HeadIndex<K, V>(new Node<K, V>(null, BASE_HEADER, null), null, null, 1); // Node.value = BASE_HEADER, 基礎層(Node層)頭節點
7     }

doPut(K, V, boolean)

方法簽名

1     private V doPut(K key, V value, boolean onlyIfAbsent) {
2         
3     }

尋找插入點,構建新結點,完成基礎層(Node層)的插入操作

 1         Node<K, V> z; // 指向待插入結點
 2         if (key == null) // 參數校驗,鍵為空,拋出空指針異常
 3             throw new NullPointerException();
 4         Comparator<? super K> cmp = comparator; // 比較器
 5         outer: for (;;) { // 外層循環
 6             for (Node<K, V> b = findPredecessor(key, cmp), n = b.next;;) { // n為當前結點,b是n的前驅結點,新結點是要插入到b和n之間的
 7                 if (n != null) {
 8                     Object v; // 指向當前結點的value
 9                     int c; // 比較key的結果
10                     Node<K, V> f = n.next; // n的後繼結點
11                     if (n != b.next) // 如果數據不一致(有別的線程修改了其前驅結點的next域)
12                         break; // 重新讀取
13                     if ((v = n.value) == null) { // 如果n被刪除
14                         n.helpDelete(b, f); // 去幫助刪除,使其盡快結束
15                         break; // 重新讀取
16                     }
17                     if (b.value == null || v == n) // b結點被刪除(其value為null,或其後繼結點是marker結點)
18                         break;
19                     if ((c = cpr(cmp, key, n.key)) > 0) { // key大於n結點的key值,因為要插入到當前結點的前面,所以不滿足要求,需要右移
20                         b = n;
21                         n = f; // 右移
22                         continue; // 繼續
23                     }
24                     if (c == 0) { // 鍵相等,替換
25                         if (onlyIfAbsent || n.casValue(v, value)) {
26                             @SuppressWarnings("unchecked")
27                             V vv = (V) v;
28                             return vv; // 返回舊值
29                         }
30                         break; // CAS失敗,有別的線程搗亂,重來
31                     }
32                 }
33 
34                 z = new Node<K, V>(key, value, n); // 構建新結點
35                 if (!b.casNext(n, z))
36                     break; // CAS失敗,有別的線程搗亂,重來
37                 break outer; // 成功,則跳出外層循環
38             }
39         }

隨機選取層(大於當前最大層,新增一層,同時更新頂層頭索引head),構建縱向索引鏈

 1         int rnd = ThreadLocalRandom.nextSecondarySeed(); // 獲取一個線程無關的隨機數,int類型,32位
 2         if ((rnd & 0x80000001) == 0) { // 最高位和最低位為1的情況下,除基礎層新增結點外,各層均不再增索引
 3             int level = 1, max;
 4             while (((rnd >>>= 1) & 1) != 0) // 低位(從第2位開始)連續為1的個數,作為選取層
 5                 ++level;
 6             Index<K, V> idx = null; // 指向從頂層開始第一個需要調整的索引,一般是選擇層level,如果level大於max,說明需要新增一層,而新增的那層(level層)不需要調整(head->HeadIndex)指向它就行,所以此時,idx指向level-1層新增的索引
 7             HeadIndex<K, V> h = head; // 頂層頭索引
 8             if (level <= (max = h.level)) { // 如果選取的層沒有超出最大層
 9                 for (int i = 1; i <= level; ++i) //構建一個從 1 層到 level層的縱縱向索引(Index)鏈
10                     idx = new Index<K, V>(z, idx, null); // 此時,idx指向level層新增的索引
11             } else { // 如果選取層超過了最大層,則增加一層索引
12                 level = max + 1; // 此時,level為老的最大層加1
13                 @SuppressWarnings("unchecked")
14                 Index<K, V>[] idxs = (Index<K, V>[]) new Index<?, ?>[level + 1]; // 用長度為level+1的數組,保存各層(1到level層,數組[0]未使用,為的是索引的層數與其所在數組的下標相等)新增索引的引用
15                 for (int i = 1; i <= level; ++i)
16                     idxs[i] = idx = new Index<K, V>(z, idx, null); // 每層新增索引保存在對應其層數的下標位置處,idx最後指向level層新增的索引
17                 for (;;) {
18                     h = head; // 再次獲取頂層頭索引
19                     int oldLevel = h.level; // 獲取老的最大層
20                     if (level <= oldLevel) // 如果選取層又比oldLevel小了,說明,別的線程搶先更新過跳表了
21                         break; // 跳出循環,idx最後指向level層的索引,同沒有超出最大層的情況
22                     HeadIndex<K, V> newh = h;
23                     Node<K, V> oldbase = h.node;
24                     for (int j = oldLevel + 1; j <= level; ++j) // 更新新增層縱向頭索引,正常情況下只新增一層
25                         newh = new HeadIndex<K, V>(oldbase, newh, idxs[j], j); // newh最後指向頂層頭索引
26                     if (casHead(h, newh)) { // CAS head(head始終指向頂層頭索引)
27                         h = newh; // h也指向頂層頭索引
28                         idx = idxs[level = oldLevel]; // idx指向老的頂層頭索引
29                         break; // 跳出循環
30                     }
31                 }
32             }

自選取層至底層,連接各層新增索引,完成跳表的構建

 1             splice: for (int insertionLevel = level;;) {
 2                 int j = h.level;
 3                 for (Index<K, V> q = h, r = q.right, t = idx;;) {
 4                     if (q == null || t == null) // 頭節點被刪除,或者新增索引為空,直接跳出外層循環
 5                         break splice;
 6                     if (r != null) {
 7                         Node<K, V> n = r.node; // 獲得右索引結點
 8                         int c = cpr(cmp, key, n.key); // 比較key
 9                         if (n.value == null) { // n(正在)被刪除
10                             if (!q.unlink(r)) // 解除r索引
11                                 break; // 如果失敗,說明有別的線程幹預,跳出內循環,重新獲取level
12                             r = q.right; // 獲取新的右索引
13                             continue; // 繼續
14                         }
15                         if (c > 0) { // key大於n結點的key值,需要右移
16                             q = r;
17                             r = r.right; // 右移
18                             continue; // 繼續
19                         }
20                     }
21 
22                     if (j == insertionLevel) {
23                         if (!q.link(r, t)) // 將t插在q和r之間
24                             break; // 如果失敗,跳出內循環,重試
25                         if (t.node.value == null) { // t索引指向的結點被刪除
26                             findNode(key);
27                             break splice; // 跳出外層循環
28                         }
29                         if (--insertionLevel == 0) // 處理結束
30                             break splice; // 跳出外層循環
31                     }
32 
33                     if (--j >= insertionLevel && j < level)
34                         t = t.down; // 處理下一層索引
35                     q = q.down; // 同步更新
36                     r = q.right; // 同步更新
37                 }
38             }

doRemove(Object, Object)

doGet(Object)

findFirst()

findPredecessor(Object, Comparator<? super K>)

findNode(Object)

tryReduceLevel()

buildFromSorted(SortedMap<K, ? extends V>)

行文至此結束。

尊重他人的勞動,轉載請註明出處:http://www.cnblogs.com/aniao/p/aniao_skip.html

【JUC源碼解析】ConcurrentSkipListMap