【JUC源碼解析】ConcurrentSkipListMap
阿新 • • 發佈:2018-05-09
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 booleanisBaseHeader() { // 是否為頭結點(每級) 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