ConcurrentSkipListMap 源码分析

ConcurrentSkipListMap

ConcurrentSkipListMap 能解决什么问题?什么时候使用 ConcurrentSkipListMap?

1)ConcurrentSkipListMap 是基于跳表实现的并发排序哈希表,映射可以根据键的自然顺序进行排序,
也可以根据创建映射时所提供的 Comparator 进行排序,具体取决于使用的构造方法。 
2)ConcurrentSkipListMap 的 containsKey、get、put、remove 操作及其变体提供预期平均 log(n) 时间开销。
3)ConcurrentSkipListMap 的功能类似于 TreeMap 的线程安全版本。
4)ConcurrentSkipListMap 提供排序功能的同时,由于其插入、删除时需要维护跳表结构,性能低于 ConcurrentHashMap。

如何使用 ConcurrentSkipListMap?

1)需要使用 ConcurrentNavigableMap 的一些特性时使用 ConcurrentSkipListMap。

使用 ConcurrentSkipListMap 有什么风险?

1)ConcurrentSkipListMap 由于底层是通过跳跃表实现的,比较耗内存。
2)新增、删除元素时需要维护跳表结构,存在一定的性能损耗。

ConcurrentSkipListMap 核心操作的实现原理?

创建实例

    /**
     *  执行键排序的比较器,如果以自然顺序排序则为 null
     */
    final Comparator<? super K> comparator;

    /** 延迟初始化的顶层索引 */
    private transient Index<K,V> head;
    /** 延迟初始化的元素计数 */
    private transient LongAdder adder;
    /** 延迟初始化的键集合 */
    private transient KeySet<K,V> keySet;
    /** 延迟初始化的值集合 */
    private transient Values<K,V> values;
    /** 延迟初始化的映射集合 */
    private transient EntrySet<K,V> entrySet;
    /** 延迟初始化的降序映射 */
    private transient SubMap<K,V> descendingMap;

    /**
     *  头结点和标记节点的 key 为 null,在节点被删除时将 val 置为null
     */
    static final class Node<K,V> {
        final K key; // currently, never detached
        V val;
        Node<K,V> next;
        Node(K key, V value, Node<K,V> next) {
            this.key = key;
            this.val = value;
            this.next = next;
        }
    }

    /**
     *  索引节点以表示跳表的层级
     */
    static final class Index<K,V> {
        // 驻留节点值
        final Node<K,V> node;  // currently, never detached
        // 下节点
        final Index<K,V> down;
        // 右节点
        Index<K,V> right;
        Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
            this.node = node;
            this.down = down;
            this.right = right;
        }
    }

    /**
     *  创建一个使用自然顺序进行排序的空 ConcurrentSkipListMap 实例
     */
    public ConcurrentSkipListMap() {
        this.comparator = null;
    }

    /**
     *  创建一个使用比较器 comparator 进行排序的空 ConcurrentSkipListMap 实例
     */
    public ConcurrentSkipListMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

添加元素

    /**
     *  1)如果目标 key 不存在,则写入新的键值对,并返回 null。
     *  2)如果目标 key 存在,则替换其关联的值,并返回旧值。
     */
    @Override
    public V put(K key, V value) {
        if (value == null) {
            throw new NullPointerException();
        }
        return doPut(key, value, false);
    }

    private V doPut(K key, V value, boolean onlyIfAbsent) {
        if (key == null) {
            throw new NullPointerException();
        }
        final Comparator<? super K> cmp = comparator;
        for (;;) {
            /**
             * h:head 头结点
             * b:predecessor 前置节点
             */
            Index<K,V> h; Node<K,V> b;
            VarHandle.acquireFence();
            // 节点所在的层次
            int levels = 0;
            // 1)head==null 表示第一次插入元素
            if ((h = head) == null) {          // try to initialize
                // 创建一个标记节点
                final Node<K,V> base = new Node<>(null, null, null);
                // 创建索引节点
                h = new Index<>(base, null, null);
                // 更新头节点
                b = ConcurrentSkipListMap.HEAD.compareAndSet(this, null, h) ? base : null;
            }
            // 2)跳表已经有元素存在
            else {
                /**
                 * q:index node
                 * r:right node
                 * d:down node
                 */
                for (Index<K,V> q = h, r, d;;) { // count while descending
                    // 索引节点的右节点不为 null
                    while ((r = q.right) != null) {
                        Node<K,V> p; K k;
                        /**
                         *  右索引节点的驻留节点为 null ||
                         *  节点的键为 null ||
                         *  节点的值为 null
                         */
                        if ((p = r.node) == null || (k = p.key) == null ||
                                p.val == null) {
                            // 删除接地啊 q 的右侧节点
                            ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, r.right);
                            // 2)查找键大于当前节点键,则继续往右侧查找
                        } else if (ConcurrentSkipListMap.cpr(cmp, key, k) > 0) {
                            q = r;
                            // 3)查找键小于等于当前键,则当层节点已经查找完毕,往下层查找
                        } else {
                            break;
                        }
                    }
                    // 1)当前索引节点存在下层节点,则往下层查找
                    if ((d = q.down) != null) {
                        // 递增层级
                        ++levels;
                        // 读取下层节点,重新进入循环并尝试往右侧查找
                        q = d;
                    }
                    else {
                        /**
                         *  2)已经到达最后一层
                         *  则读取驻留其上的节点值,开始玩右侧遍历
                         */
                        b = q.node;
                        break;
                    }
                }
            }
            // 前置节点存在
            if (b != null) {
                Node<K,V> z = null;              // new node, if inserted
                for (;;) {
                    /**
                     * n:node
                     * k:key
                     * v:value
                     * c:Comparisons 比较值
                     */
                    Node<K,V> n, p; K k; V v; int c;
                    // 1)前置节点的 next 为 null,则表示当前节点是最后一个节点
                    if ((n = b.next) == null) {
                        if (b.key == null) {
                            ConcurrentSkipListMap.cpr(cmp, key, key);
                        }
                        c = -1;
                    }
                    // 2)节点键为 null
                    else if ((k = n.key) == null) {
                        break;                   // can't append; restart
                        // 3)节点值为 null,该节点已经被删除,需要从链表中踢除
                    } else if ((v = n.val) == null) {
                        ConcurrentSkipListMap.unlinkNode(b, n);
                        c = 1;
                    }
                    // 4)比较查找键和当前键,并且查找键比较大
                    else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
                        // 查找下一个节点
                        b = n;
                        /**
                         * 5)如果键相等 && 只有不存在才插入元素,则直接返回;否则尝试更新节点值
                         */
                    } else if (c == 0 &&
                            (onlyIfAbsent || ConcurrentSkipListMap.VAL.compareAndSet(n, v, value))) {
                        // 更新成功则返回旧值
                        return v;
                    }
                    /**
                     *  目标键大于 predecessor.key 小于  predecessor.next.key,
                     *  将目标键值对插入到他们中间。
                     */
                    if (c < 0 &&
                            ConcurrentSkipListMap.NEXT.compareAndSet(b, n,
                                    p = new Node<>(key, value, n))) {
                        // 读取新增节点
                        z = p;
                        break;
                    }
                }

                if (z != null) {
                    // 读取随机数
                    final int lr = ThreadLocalRandom.nextSecondarySeed();
                    // 有 1/4 的机会基于新增节点生成索引节点
                    if ((lr & 0x3) == 0) {       // add indices with 1/4 prob
                        final int hr = ThreadLocalRandom.nextSecondarySeed();
                        long rnd = (long)hr << 32 | lr & 0xffffffffL;
                        // 新增节点所在的层级,层级从 0 开始
                        int skips = levels;      // levels to descend before add
                        Index<K,V> x = null;
                        for (;;) {               // create at most 62 indices
                            // 基于新增节点生成顶层索引节点
                            x = new Index<>(z, x, null);
                            if (rnd >= 0L || --skips < 0) {
                                break;
                            } else {
                                rnd <<= 1;
                            }
                        }
                        /**
                         *  新增索引节点成功 &&
                         *  如果是新增顶层索引节点 &&
                         *  增加新的一层
                         */
                        if (ConcurrentSkipListMap.addIndices(h, skips, x, cmp) && skips < 0 &&
                                head == h) {         // try to add new level
                            // 将新增节点加到顶层
                            final Index<K,V> hx = new Index<>(z, x, null);
                            // 创建新的头节点
                            final Index<K,V> nh = new Index<>(h.node, h, hx);
                            // 更新头节点
                            ConcurrentSkipListMap.HEAD.compareAndSet(this, h, nh);
                        }
                        if (z.val == null)
                        {
                            findPredecessor(key, cmp); // clean
                        }
                    }
                    // 增加计数值
                    addCount(1L);
                    return null;
                }
            }
        }
    }

    /**
     *  在插入元素之后新增索引节点,从高层向低层递归插入,之后建立和前置节点的链接
     *  Recursion depths are exponentially less probable.
     *
     * @param q 当前层级的起始索引
     * @param skips 插入索引时,需要跳过的层级数
     * @param x 插入的目标索引
     * @param cmp comparator
     */
    static <K,V> boolean addIndices(Index<K,V> q, int skips, Index<K,V> x,
            Comparator<? super K> cmp) {
        Node<K,V> z; K key;
        /**
         * 1)新增索引节点不为 null &&
         * 2)驻留数据节点不为 null &&
         * 3)数据节点的键不为 null &&
         * 4)起始索引节点不为 null
         */
        if (x != null && (z = x.node) != null && (key = z.key) != null &&
                q != null) {                            // hoist checks
            boolean retrying = false;
            for (;;) {                              // find splice point
                /**
                 * r:right
                 * d:down
                 * c:Comparisons
                 */
                Index<K,V> r, d; int c;
                /**
                 *  当前节点的右侧节点不为 null
                 */
                if ((r = q.right) != null) {
                    Node<K,V> p; K k;
                    // 1)尝试删除索引节点
                    if ((p = r.node) == null || (k = p.key) == null ||
                            p.val == null) {
                        ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, r.right);
                        c = 0;
                    }
                    // 2)目标键比当前节点键大
                    else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
                        // 往右侧查找
                        q = r;
                        //  3)目标键和当前键相等
                    } else if (c == 0) {
                        break;                      // stale
                    }
                    // 4)目标键比当前节点键小?
                } else {
                    // 已经不存在右侧节点
                    c = -1;
                }

                if (c < 0) {
                    // 下节点不为 null && 层级数 > 0
                    if ((d = q.down) != null && skips > 0) {
                        // 递减层级后,往下查找
                        --skips;
                        q = d;
                    }
                    /**
                     *  1)下节点不为 null && skip <=0 && 未出现索引添加失败 &&
                     *  尝试在当前层级添加索引
                     */
                    else if (d != null && !retrying &&
                            !ConcurrentSkipListMap.addIndices(d, 0, x.down, cmp)) {
                        // 索引添加失败则退出
                        break;
                    } else {
                        x.right = r;
                        // 插入新增索引节点
                        if (ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, x)) {
                            // 执行成功则退出
                            return true;
                        }
                        else {
                            retrying = true;         // re-find splice point
                        }
                    }
                }
            }
        }
        return false;
    }

读取元素

    /**
     *  返回指定 key 映射的值,如果 key 不存在,则返回 null
     */
    @Override
    public V get(Object key) {
        return doGet(key);
    }

    /**
     * Gets value for key. Same idea as findNode, except skips over
     * deletions and markers, and returns first encountered value to
     * avoid possibly inconsistent rereads.
     */
    private V doGet(Object key) {
        Index<K,V> q;
        VarHandle.acquireFence();
        if (key == null) {
            throw new NullPointerException();
        }
        final Comparator<? super K> cmp = comparator;
        V result = null;
        if ((q = head) != null) {
            outer: for (Index<K,V> r, d;;) {
                /**
                 * 首先向右侧查找,之后向下查找
                 */
                while ((r = q.right) != null) {
                    Node<K,V> p; K k; V v; int c;
                    // 节点已经被删除
                    if ((p = r.node) == null || (k = p.key) == null ||
                            (v = p.val) == null) {
                        ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, r.right);
                    // 目标键 > 节点键,则向右侧查找 
                    } else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
                        q = r;
                    // 找到目标键    
                    } else if (c == 0) {
                        // 读取值后返回
                        result = v;
                        break outer;
                    } else {
                        break;
                    }
                }
                // 尝试向下层查找
                if ((d = q.down) != null) {
                    q = d;
                } else {
                    // 已经到达底层
                    Node<K,V> b, n;
                    // 读取索引节点驻留的数据节点之后,往右侧遍历查找
                    if ((b = q.node) != null) {
                        while ((n = b.next) != null) {
                            V v; int c;
                            final K k = n.key;
                            // 跳过被删除节点和标记节点,如果目标键 > 节点键,也向右侧查找
                            if ((v = n.val) == null || k == null ||
                                    (c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
                                b = n;
                            // 目标键 <= 节点键   
                            } else {
                                // 如果相等,则直接返回其值
                                if (c == 0) {
                                    result = v;
                                }
                                // 不存在相等的键,则返回 null
                                break;
                            }
                        }
                    }
                    break;
                }
            }
        }
        return result;
    }

    /**
     *  返回目标 key 关联的值,如果键不存在,则返回 defaultValue
     */
    @Override
    public V getOrDefault(Object key, V defaultValue) {
        V v;
        return (v = doGet(key)) == null ? defaultValue : v;
    }

替换值

    /**
     *  如果目标 key 存在,则替换其值为 value,并返回旧值;否则返回 null。
     */
    @Override
    public V replace(K key, V value) {
        // 键和值都非 null
        if (key == null || value == null) {
            throw new NullPointerException();
        }
        for (;;) {
            Node<K,V> n; V v;
            // 没找到目标键
            if ((n = findNode(key)) == null) {
                // 则返回 null
                return null;
            }
            // 尝试原子设置值,并返回旧值
            if ((v = n.val) != null && ConcurrentSkipListMap.VAL.compareAndSet(n, v, value)) {
                return v;
            }
        }
    }

    /**
     *  返回指定 key 映射的节点,如果键不存在,则返回 null。
     *  查找过程中会踢除已经被删除键值对的索引节点,并且重新通过 findPredecessor 方法
     *  查找基础节点进行再次遍历【如果节点的 key 为 null,则表示它是一个标记节点,
     *  它的前置节点将被删除。】
     *  The traversal loops in doPut, doRemove, and findNear all include the same checks.
     */
    private Node<K,V> findNode(Object key) {
        if (key == null)
        {
            throw new NullPointerException(); // don't postpone errors
        }
        final Comparator<? super K> cmp = comparator;
        Node<K,V> b;
        // 读取底层有效的前置节点
        outer: while ((b = findPredecessor(key, cmp)) != null) {
            for (;;) {
                Node<K,V> n; K k; V v; int c;
                // 1)已经到达尾部,则直接退出
                if ((n = b.next) == null) {
                    break outer;               // empty
                // 2)当前节点是标记节点  
                } else if ((k = n.key) == null) {
                    break;                     // b is deleted
                // 3)当前索引节点驻留的数据节点关联的键值对已经被删除,则踢除当前节点   
                } else if ((v = n.val) == null) {
                    ConcurrentSkipListMap.unlinkNode(b, n);          // n is deleted
                // 4)目标键 > 节点键,则往右侧查找
                } else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
                    b = n;
                // 5)找到目标节点,则直接返回   
                } else if (c == 0) {
                    return n;
                } else {
                    break outer;
                }
            }
        }
        return null;
    }

    /**
     *  删除节点 n
     */
    static <K,V> void unlinkNode(Node<K,V> b, Node<K,V> n) {
        if (b != null && n != null) {
            Node<K,V> f, p;
            for (;;) {
                if ((f = n.next) != null && f.key == null) {
                    p = f.next;               // already marked
                    break;
                }
                // 被删除节点是尾节点
                else if (ConcurrentSkipListMap.NEXT.compareAndSet(n, f,
                        new Node<>(null, null, f))) {
                    p = f;                    // add marker
                    break;
                }
            }
            ConcurrentSkipListMap.NEXT.compareAndSet(b, n, p);
        }
    }

    /**
     *  如果目标 key 存在 && 旧值为  oldValue,则替换其值为 value,替换成功返回 true
     */
    @Override
    public boolean replace(K key, V oldValue, V newValue) {
        if (key == null || oldValue == null || newValue == null) {
            throw new NullPointerException();
        }
        for (;;) {
            Node<K,V> n; V v;
            // 节点不存在
            if ((n = findNode(key)) == null) {
                return false;
            }
            if ((v = n.val) != null) {
                // 节点值和 oldValue 不相等
                if (!oldValue.equals(v)) {
                    return false;
                }
                // 原子更新旧值
                if (ConcurrentSkipListMap.VAL.compareAndSet(n, v, newValue)) {
                    return true;
                }
            }
        }
    }

删除键值对

    /**
     *  如果指定的 key 存在,则删除键值对,并返回旧值;否则返回 null
     */
    @Override
    public V remove(Object key) {
        return doRemove(key, null);
    }

    /**
     *  定位节点,将其值置为 null,在其后添加一个删除标记节点,断开前置节点,
     *  移除关联的索引节点,并尝试递减层级。
     */
    final V doRemove(Object key, Object value) {
        if (key == null) {
            throw new NullPointerException();
        }
        final Comparator<? super K> cmp = comparator;
        V result = null;
        Node<K,V> b;
        outer: while ((b = findPredecessor(key, cmp)) != null &&
                result == null) {
            for (;;) {
                Node<K,V> n; K k; V v; int c;
                // 1)无后继节点
                if ((n = b.next) == null) {
                    break outer;
                // 2)当前节点是一个删除标记节点
                } else if ((k = n.key) == null) {
                    break;
                // 3)当前节点的数据节点被删除,则将其踢除 
                } else if ((v = n.val) == null) {
                    ConcurrentSkipListMap.unlinkNode(b, n);
                // 4)目标键 > 节点键,则往右侧查找   
                } else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
                    b = n;
                // 5)无匹配键,则直接返回 
                } else if (c < 0) {
                    break outer;
                // 6)如果值匹配,则删除的场景   
                } else if (value != null && !value.equals(v)) {
                    break outer;
                // 7)将节点值置为 null    
                } else if (ConcurrentSkipListMap.VAL.compareAndSet(n, v, null)) {
                    // 读取旧值
                    result = v;
                    // 踢除节点
                    ConcurrentSkipListMap.unlinkNode(b, n);
                    break; // loop to clean up
                }
            }
        }
        if (result != null) {
            // 尝试递减层级
            tryReduceLevel();
            addCount(-1L);
        }
        return result;
    }

    /**
     *  如果指定的 key 存在 && 键关联的值和 value 相等,则删除键值对,并返回 true。
     */
    @Override
    public boolean remove(Object key, Object value) {
        if (key == null) {
            throw new NullPointerException();
        }
        return value != null && doRemove(key, value) != null;
    }
原文地址:https://www.cnblogs.com/zhuxudong/p/10073536.html