【JUC】JDK1.8源碼分析之ConcurrentSkipListMap(二)


一、前言

  最近在做項目的同時也在修復之前項目的一些Bug,所以忙得沒有時間看源代碼,今天都完成得差不多了,所以又開始源碼分析之路,也着筆記錄下ConcurrentSkipListMap的源碼的分析過程。

二、ConcurrentSkipListMap數據結構

  抓住了數據結構,對於理解整個ConcurrentSkipListMap有很重要的作用,其實,通過源碼可知其數據結構如下。

  說明:可以看到ConcurrentSkipListMap的數據結構使用的是跳表,每一個HeadIndex、Index結點都會包含一個對Node的引用,同一垂直方向上的Index、HeadIndex結點都包含了最底層的Node結點的引用。並且層級越高,該層級的結點(HeadIndex和Index)數越少。Node結點之間使用單鏈表結構。

三、ConcurrentSkipListMap源碼分析

  3.1 類的繼承關系 

public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable {}

  說明:ConcurrentSkipListMap繼承了AbstractMap抽象類,實現了ConcurrentNavigableMap接口,該接口定義了獲取某一部分集合的操作。實現了Cloneable接口,表示允許克隆。實現了Serializable接口,表示可被序列化。

  3.2 類的內部類

  ConcurrentSkipListMap包含了很多內部類,內部類的框架圖如下:

  說明:其中,最為重要的類包括Index、HeadIndex、Node三個類。下面對這三個類進行逐一講解,其他的類,讀者有興趣可以自行分析。

  ① Index類

  1. 類的屬性 

    static class Index<K,V> {
        final Node<K,V> node;
        final Index<K,V> down;
        volatile Index<K,V> right;
        
        // Unsafe mechanics
        private static final sun.misc.Unsafe UNSAFE;
        private static final long rightOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = Index.class;
                rightOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("right"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

  說明:可以看到,Index結點包括一個Node結點的引用,都是包含down域和right域,即對應數據結構中的Index結點。並且借助了反射來原子性的修改right域。

  2. 類的構造函數   

 /**
         * Creates index node with given values.
         */
        Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
            this.node = node;
            this.down = down;
            this.right = right;
        }
View Code

 說明:構造Index結點,確定Node引用,down域和right域。

  3. 核心函數分析

  3.1 link函數 

        final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
            // 獲取Index結點的Node結點
            Node<K,V> n = node;
            // 將newSucc結點的right域設置為succ
            newSucc.right = succ;
            // 結點的值不為空並且比較並交換當前Index結點的right域(將當前Index(this)結點的right域設置為newSucc)
            return n.value != null && casRight(succ, newSucc);
        }
View Code

  說明:link方法用於在當前Index結點的后面插入一個Index結點,形成其right結點。並且插入的Index結點的right域為當前結點的right域。

  3.2 unlink函數  

        final boolean unlink(Index<K,V> succ) {
            // 當前Index結點的Node結點的值不為空並且將當前Index結點的right設置為succ的right結點
            return node.value != null && casRight(succ, succ.right);
        }
View Code

  說明:unlink方法與link方法作用相反,其刪除當前Index結點的right結點,即將當前Index結點的right指向當前Index結點的right.right域。

  ② HeadIndex類  

    // 頭結點索引類
    static final class HeadIndex<K,V> extends Index<K,V> {
        // 層級
        final int level;
        // 構造函數
        HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
            // 構造Index類
            super(node, down, right);
            this.level = level;
        }
    }

  說明:根據HeadIndex類可知其繼承自Index類,並且在Index類的基礎上添加了level域,表示當前的層級。

  ③ Node類

  1. 類的屬性  

    static final class Node<K,V> {
        //
        final K key;
        //
        volatile Object value;
        // 下一個結點
        volatile Node<K,V> next;
        
        // UNSAFE mechanics

        private static final sun.misc.Unsafe UNSAFE;
        // value域的偏移地址
        private static final long valueOffset;
        // next域的偏移地址
        private static final long nextOffset;

        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = Node.class;
                valueOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("value"));
                nextOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }
View Code

  說明:Node類包含了key、value、next域,其是用來實際存放元素的結點,並且是使用單鏈表結構。同時,也使用了反射來原子性的修改value與和next域。

  2. 類的構造函數   

        Node(K key, Object value, Node<K,V> next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }

        /**
         * Creates a new marker node. A marker is distinguished by
         * having its value field point to itself.  Marker nodes also
         * have null keys, a fact that is exploited in a few places,
         * but this doesn't distinguish markers from the base-level
         * header node (head.node), which also has a null key.
         */
        // 用於建立標記結點,值為本身
        Node(Node<K,V> next) {
            this.key = null;
            this.value = this;
            this.next = next;
        }
View Code

  說明:Node類包含了兩種構造函數,分別表示正常的結點和marker標記結點,marker標記結點在刪除結點時被使用。

  3. 類的核心函數

  3.1 helpDelete函數 

        void helpDelete(Node<K,V> b, Node<K,V> f) {
            /*
             * Rechecking links and then doing only one of the
             * help-out stages per call tends to minimize CAS
             * interference among helping threads.
             */
            if (f == next && this == b.next) { // f為當前結點的后繼並且b為當前結點的前驅
                if (f == null || f.value != f) // f為空或者f的value不為本身,即沒有被標記    not already marked
                    // 當前結點后添加一個marker結點,並且當前結點的后繼為marker,marker結點的后繼為f
                    casNext(f, new Node<K,V>(f)); 
                else // f不為空並且f的值為本身
                    // 設置b的next域為f的next域
                    b.casNext(this, f.next);
            }
        }
View Code

  說明:刪除結點,在結點后面添加一個marker結點或者將結點和其后的marker結點從其前驅中斷開。

  3.3 類的屬性  

public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable {
    // 版本序列號    
    private static final long serialVersionUID = -8627078645895051609L;
    // 基礎層的頭結點
    private static final Object BASE_HEADER = new Object();
    // 最頂層頭結點的索引
    private transient volatile HeadIndex<K,V> head;
    // 比較器
    final Comparator<? super K> comparator;
    // 鍵集合
    private transient KeySet<K> keySet;
    // entry集合
    private transient EntrySet<K,V> entrySet;
    // 值集合
    private transient Values<V> values;
    // 降序鍵集合
    private transient ConcurrentNavigableMap<K,V> descendingMap;
    
    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    // head域的偏移量
    private static final long headOffset;
    // Thread類的threadLocalRandomSecondarySeed的偏移量
    private static final long SECONDARY;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = ConcurrentSkipListMap.class;
            headOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("head"));
            Class<?> tk = Thread.class;
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));

        } catch (Exception e) {
            throw new Error(e);
        }
    }
}
View Code

  說明:ConcurrentSkipListMap包含了head屬性,表示跳表的頭結點,並且包含了一個比較器,值得注意的是,對於ConcurrentSkipListMap的使用,鍵必須能夠進行比較,如傳遞了比較器或者鍵本身就能夠進行比較。同時,也使用了反射來保證原子性的更新head域。

  3.4 類的構造函數

  1. ConcurrentSkipListMap()型構造函數  

    // 構造一個新的空映射,該映射按照鍵的自然順序進行排序
    public ConcurrentSkipListMap() {
        // 比較器為空,那么鍵必須能夠比較(實現了Comparable接口)
        this.comparator = null;
        // 初始化相關的域
        initialize();
    }
View Code

  說明:構造一個新的空映射,該映射按照鍵的自然順序進行排序,即鍵K必須實現了Comparable接口,否則,會報錯。

  2. ConcurrentSkipListMap(Comparator<? super K>)型構造函數  

    // 構造一個新的空映射,該映射按照指定的比較器進行排序
    public ConcurrentSkipListMap(Comparator<? super K> comparator) {
        // 初始化比較器
        this.comparator = comparator;
        // 初始化相關的域
        initialize();
    }
View Code

  說明:構造一個新的空映射,該映射按照指定的比較器進行排序

  3. ConcurrentSkipListMap(Map<? extends K, ? extends V>)型構造函數  

    // 構造一個新映射,該映射所包含的映射關系與給定映射包含的映射關系相同,並按照鍵的自然順序進行排序
    public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
        // 比較器Wie空
        this.comparator = null;
        // 初始化相關的域
        initialize();
        // 將m的所有元素添加至跳表
        putAll(m);
    }
View Code

  說明:構造一個新映射,該映射所包含的映射關系與給定映射包含的映射關系相同,並按照鍵的自然順序進行排序。

  4. ConcurrentSkipListMap(SortedMap<K, ? extends V>)型構造函數  

    // 構造一個新映射,該映射所包含的映射關系與指定的有序映射包含的映射關系相同,使用的順序也相同
    public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
        // 獲取m的比較器
        this.comparator = m.comparator();
        // 初始化相關的域
        initialize();
        // 根據m的元素來構建跳表
        buildFromSorted(m);
    }
View Code

  說明:構造一個新映射,該映射所包含的映射關系與指定的有序映射包含的映射關系相同,使用的順序也相同。

  3.5 核心函數分析

  1. doPut函數  

    // 插入一個結點
    private V doPut(K key, V value, boolean onlyIfAbsent) {
        Node<K,V> z;             // added node
        if (key == null) // 鍵為空,拋出空異常
            throw new NullPointerException();
        // 比較器
        Comparator<? super K> cmp = comparator;
        outer: for (;;) { // 無限循環
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) { // 找到先驅結點,n為當前結點
                if (n != null) { // next域不為空
                    Object v; int c;
                    // f為當前結點的后繼節點
                    Node<K,V> f = n.next;
                    if (n != b.next)  // 不一致,重試            // inconsistent read
                        break;
                    if ((v = n.value) == null) { // n結點已經被刪除  // n is deleted
                        // 進行刪除
                        n.helpDelete(b, f);
                        break;
                    }
                    if (b.value == null || v == n) // b結點已經被刪除        b is deleted
                        break;
                    if ((c = cpr(cmp, key, n.key)) > 0) { // key大於結點的key
                        // b往后移動
                        b = n;
                        // n往后移動
                        n = f;
                        continue;
                    }
                    if (c == 0) { // 鍵相等
                        if (onlyIfAbsent || n.casValue(v, value)) { // 比較並交換值
                            @SuppressWarnings("unchecked") V vv = (V)v;
                            return vv;
                        }
                        // 重試
                        break; // restart if lost race to replace value
                    }
                    // else c < 0; fall through
                }
                // 新生一個結點
                z = new Node<K,V>(key, value, n);
                if (!b.casNext(n, z)) // 比較並交換next域
                    break;         // restart if lost race to append to b
                // 成功,則跳出循環
                break outer;
            }
        }
        // 隨機生成種子
        int rnd = ThreadLocalRandom.nextSecondarySeed();
        if ((rnd & 0x80000001) == 0) { // test highest and lowest bits
            int level = 1, max;
            while (((rnd >>>= 1) & 1) != 0) // 判斷從右到左有多少個連續的1
                ++level;
            Index<K,V> idx = null;
            // 保存頭結點
            HeadIndex<K,V> h = head;
            if (level <= (max = h.level)) { // 小於跳表的層級
                for (int i = 1; i <= level; ++i) // 為結點生成對應的Index結點
                    // 從下至上依次賦值,並且賦值了Index結點的down域
                    idx = new Index<K,V>(z, idx, null);
            }
            else { // try to grow by one level
                level = max + 1; // hold in array and later pick the one to use
                // 生成Index結點的數組,其中,idxs[0]不作使用
                @SuppressWarnings("unchecked")Index<K,V>[] idxs =
                    (Index<K,V>[])new Index<?,?>[level+1]; 
                for (int i = 1; i <= level; ++i) // 從下到上生成Index結點,並賦值down域
                    idxs[i] = idx = new Index<K,V>(z, idx, null);
                for (;;) { // 無限循環
                    // 保存頭結點
                    h = head;
                    // 保存跳表之前的層級
                    int oldLevel = h.level;
                    if (level <= oldLevel) // lost race to add level
                        break;
                    // 保存頭結點
                    HeadIndex<K,V> newh = h;
                    // 保存頭結點對應的Node結點
                    Node<K,V> oldbase = h.node;
                    for (int j = oldLevel+1; j <= level; ++j) // 為每一層生成一個頭結點
                        newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
                    if (casHead(h, newh)) { // 比較並替換頭結點
                        // h賦值為最高層的頭結點
                        h = newh;
                        // idx賦值為之前層級的頭結點,並將level賦值為之前的層級
                        idx = idxs[level = oldLevel];
                        break;
                    }
                }
            }
            // find insertion points and splice in
            // 插入Index結點
            splice: for (int insertionLevel = level;;) { 
                // 保存新跳表的層級
                int j = h.level;
                for (Index<K,V> q = h, r = q.right, t = idx;;) { 
                    if (q == null || t == null) // 頭結點或者idx結點為空
                        // 跳出外層循環
                        break splice;
                    if (r != null) { // right結點不為空
                        // 保存r對應的Node結點
                        Node<K,V> n = r.node;
                        // compare before deletion check avoids needing recheck
                        // 比較key與結點的key值
                        int c = cpr(cmp, key, n.key);
                        if (n.value == null) { // 結點的值為空,表示需要刪除
                            if (!q.unlink(r)) // 刪除q的Index結點
                                break;
                            // r為q的right結點
                            r = q.right;
                            continue;
                        }
                        if (c > 0) { // key大於結點的key
                            // 向右尋找
                            q = r;
                            r = r.right;
                            continue;
                        }
                    }

                    if (j == insertionLevel) {
                        if (!q.link(r, t)) // r結點插入q與t之間
                            break; // restart
                        if (t.node.value == null) { // t結點的值為空,需要刪除
                            // 利用findNode函數的副作用
                            findNode(key);
                            break splice;
                        }
                        if (--insertionLevel == 0) // 到達最底層,跳出循環
                            break splice;
                    }

                    if (--j >= insertionLevel && j < level) 
                        t = t.down;
                    q = q.down;
                    r = q.right;
                }
            }
        }
        return null;
    }
View Code

  說明:doPut提供對put函數的支持,doPut的大體流程如下:

  ① 根據給定的key從跳表的左上方往右或者往下查找到Node鏈表的前驅Node結點,這個查找過程會刪除一些已經標記為刪除的結點。

  ② 找到前驅結點后,開始往后插入查找插入的位置(因為找到前驅結點后,可能有另外一個線程在此前驅結點后插入了一個結點,所以步驟①得到的前驅現在可能不是要插入的結點的前驅,所以需要往后查找)。

  ③ 隨機生成一個種子,判斷是否需要增加層級,並且在各層級中插入對應的Index結點。

  其中,會調用到findPredecessor函數,findPredecessor函數源碼如下  

    private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
        if (key == null) // 鍵為空,拋出空異常
            throw new NullPointerException(); // don't postpone errors
        for (;;) { // 無限循環
            for (Index<K,V> q = head, r = q.right, d;;) { // 
                if (r != null) { // 右Index結點不為空
                    // n為當前Node結點
                    Node<K,V> n = r.node;
                    // 為當前key
                    K k = n.key;
                    if (n.value == null) { // 當前Node結點的value為空,表示需要刪除
                        if (!q.unlink(r)) // unlink r Index結點
                            break;           // restart
                        // r為rightIndex結點
                        r = q.right;         // reread r
                        continue;
                    }
                    if (cpr(cmp, key, k) > 0) { // 比較key與當前Node結點的k,若大於0
                        // 向右移動
                        q = r;
                        r = r.right;
                        continue;
                    }
                }
                if ((d = q.down) == null) // q的down域為空,直接返回q對應的Node結點
                    return q.node;
                // 向下移動
                q = d;
                // d的right結點
                r = d.right;
            }
        }
    }
View Code

  說明:findPredecessor函數的主要流程如下。

  從頭結點(head)開始,先比較key與當前結點的key的大小,若key大於當前Index結點的key並且當前Index結點的right不為空,則向右移動,繼續查找;若當前Index結點的right為空,則向下移動,繼續查找;若key小於等於當前Index結點的key,則向下移動,繼續查找。直至找到前驅結點。

  2. doRemove函數  

    // 移除一個結點
    final V doRemove(Object key, Object value) {
        if (key == null)
            throw new NullPointerException();
        // 保存比較器
        Comparator<? super K> cmp = comparator;
        outer: for (;;) { // 無限循環
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) { // 根據key找到前驅結點,n為當前Index結點
                Object v; int c;
                if (n == null) // n不為空
                    break outer;
                // f為當前結點的next結點
                Node<K,V> f = n.next; 
                if (n != b.next)  // 不一致,重試                  // inconsistent read
                    break;
                if ((v = n.value) == null) {  // 當前結點的value為空,需要刪除      // n is deleted
                    // 刪除n結點
                    n.helpDelete(b, f);
                    break;
                }
                if (b.value == null || v == n)      // b is deleted
                    break;
                if ((c = cpr(cmp, key, n.key)) < 0) // key小於當前結點的key
                    // 跳出外層循環
                    break outer;
                if (c > 0) { // key大於當前結點的key
                    // 向后移動
                    b = n;
                    n = f;
                    continue;
                }
                if (value != null && !value.equals(v))
                    break outer;
                if (!n.casValue(v, null)) // 當前結點的value設置為null
                    break;
                if (!n.appendMarker(f) || !b.casNext(n, f)) // 在n結點后添加一個marker結點,並且將b的next域更新為f
                    findNode(key);                  // retry via findNode
                else { // 添加節點並且更新均成功
                    // 利用findNode函數的副作用,刪除n結點對應的Index結點
                    findPredecessor(key, cmp);      // clean index
                    if (head.right == null) // 頭結點的right為null
                        // 需要減少層級
                        tryReduceLevel();
                }
                @SuppressWarnings("unchecked") V vv = (V)v;
                return vv;
            }
        }
        return null;
    }
View Code

  說明:doRemove函數的處理流程如下。

  ① 根據key值找到前驅結點,查找的過程會刪除一個標記為刪除的結點。

  ② 從前驅結點往后查找該結點。

  ③ 在該結點后面添加一個marker結點,若添加成功,則將該結點的前驅的后繼設置為該結點之前的后繼。

  ④ 頭結點的next域是否為空,若為空,則減少層級。

  下面的示意圖給出了remove操作一種可能的情況(僅僅涉及Node結點的鏈表層的操作)

  說明:可以看到remove操作是分為兩步進行的,首先是在要刪除結點的后面添加一個marker結點,然后修改刪除結點的前驅結點的next域。注意,這里僅僅只給出了Node結點的鏈表層的操作,並沒有涉及到Index結點,關於Index結點的情況,之后會給出一個示例。其中會調用到tryReduceLevel函數,tryReduceLevel源碼如下

    // 減少跳表層級
    private void tryReduceLevel() {
        // 保存頭結點
        HeadIndex<K,V> h = head;
        HeadIndex<K,V> d;
        HeadIndex<K,V> e;
        if (h.level > 3 &&
            (d = (HeadIndex<K,V>)h.down) != null &&
            (e = (HeadIndex<K,V>)d.down) != null &&
            e.right == null &&
            d.right == null &&
            h.right == null &&
            casHead(h, d) && // try to set
            h.right != null) // recheck
            casHead(d, h);   // try to backout
    }
View Code

  說明:如果最高的前三個HeadIndex不為空,並且其right域都為null,那么就將level減少1層,並將head設置為之前head的下一層,設置完成后,還有檢測之前的head的right域是否為null,如果為null,則減少層級成功,否則再次將head設置為h。
  3. doGet函數  

    private V doGet(Object key) {
        if (key == null)
            throw new NullPointerException();
        Comparator<? super K> cmp = comparator;
        outer: for (;;) {
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) { // 根據key找到前驅結點,n為當前結點
                Object v; int c;
                if (n == null) // 當前Index結點為null,跳出外層循環
                    break outer;
                // f為當前結點的next結點
                Node<K,V> f = n.next;
                if (n != b.next)    // 不一致,重試                // inconsistent read
                    break;
                if ((v = n.value) == null) {    // n is deleted
                    n.helpDelete(b, f);
                    break;
                }
                if (b.value == null || v == n)  // b is deleted
                    break;
                if ((c = cpr(cmp, key, n.key)) == 0) { // 找到key值相等的結點
                    @SuppressWarnings("unchecked") V vv = (V)v;
                    // 返回value
                    return vv;
                }
                if (c < 0) // 小於當前結點
                    // 則表示沒有找到,跳出外層循環
                    break outer;
                // 繼續向后移動
                b = n;
                n = f;
            }
        }
        return null;
    }
View Code

  說明:doGet函數流程比較簡單,首先根據key找到前驅結點,然后從前驅結點開始往后查找,找到與key相等的結點,則返回該結點,否則,返回null。在這個過程中會刪除一些已經標記為刪除狀態的結點。

  4. size函數  

    public int size() {
        long count = 0;
        for (Node<K,V> n = findFirst(); n != null; n = n.next) { // 找到第一個結點
            if (n.getValidValue() != null) // n結點沒有被標記刪除
                // 計數器加1
                ++count;
        }
        return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;
    }
View Code

  說明:size函數的流程如下,首先利用findFirst函數找到第一個value不為null的結點。然后開始往后遍歷,調用Node結點的getValidValue函數判斷結點的value是否有效,有效則計數器加1。其中findFirst源碼如下  

    final Node<K,V> findFirst() {
        for (Node<K,V> b, n;;) {
            if ((n = (b = head.node).next) == null) // 頭結點的下一個結點為當前結點,為null
                // 返回null
                return null;
            if (n.value != null) // 當前結點不為null
                // 則返回該結點
                return n;
            // 表示當前結點的value為null,則進行刪除
            n.helpDelete(b, n.next);
        }
    }
View Code

  說明:findFirst函數的功能是找到第一個value不為null的結點。getValidValue源碼如下 

        V getValidValue() {
            Object v = value;
            if (v == this || v == BASE_HEADER) // value為自身或者為BASE_HEADER
                return null;
            @SuppressWarnings("unchecked") V vv = (V)v;
            return vv;
        }
View Code

  說明:若結點的value為自身或者是BASE_HEADER,則返回null,否則返回結點的value。

四、示例

  下面通過一個簡單的示例,來深入了解ConcurrentSkipListMap的內部結構。 

package com.hust.grid.leesf.collections;

import java.util.concurrent.ConcurrentSkipListMap;

public class ConcurrentSkipListMapDemo {
    public static void main(String[] args) {
        ConcurrentSkipListMap<String, Integer> cslm = new ConcurrentSkipListMap<String, Integer>();
        cslm.put("leesf", 24);
        cslm.put("dyd", 24);
        
        for (String key :cslm.keySet()) {
            System.out.print("[" + key + "," + cslm.get(key) + "] ");
        }
        System.out.println();
        cslm.remove("leesf");
        for (String key :cslm.keySet()) {
            System.out.print("[" + key + "," + cslm.get(key) + "] ");
        }
    }
}
View Code

  運行結果:

[dyd,24] [leesf,24] 
[dyd,24]

  說明:上面的一個示例非常簡單,下面借這個示例,來分析ConcurrentSkipListMap的內部結構。

  ① 當新生一個ConcurrentSkipListMap時,有如下結構。

  ② 當put("leesf", 24)后,可能有如下結構

  

  說明:在插入一個Node結點的同時,也插入一個Index結點,並且head結點的right域指向該Index結點,該Index的Node域指向插入的Node結點。

   ③ 當put("dyd", 24)后,可能有如下結構

  說明:插入的("dyd", 24),新生成的結點在leesf結點之前,並且也生成了一個Index結點指向它,此時跳表的層級還是為1。

  ④ 同樣,當put("dyd", 24)后,也可能有如下結構 

  說明:在插入("dyd", 24)后,層級加1,此時會生成兩個Index結點,並且兩個Index結點均指向新生成的Node結點。

  ⑤ 在remove("dyd")之后,存在如下結構

  說明:在key為dyd的結點后面添加了一個marker結點(key為null,value為自身),並且Node結點對應的Index也將從Index鏈表中斷開,最后會被GC。

 五、總結

  看源代碼需要耐心,多思考,才能領略源碼的魅力所在,也謝謝各位園友的觀看~

 

參考鏈接:http://brokendreams.iteye.com/blog/2253955

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM