zoukankan      html  css  js  c++  java
  • 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;
        }
    
  • 相关阅读:
    关于guava实现线程池
    结构化方法与面向对象方法的比较
    敏捷开发与传统开发方式的比较
    C# 事件的使用方法
    C# 泛型反射的调用
    RPC 知识科普一下
    C#枚举(Enum)小结
    C#图片添加文字水印
    C#索引器
    C#隐式转换与显示转换
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10073536.html
Copyright © 2011-2022 走看看