JAVA并发容器-ConcurrentSkipListMap,ConcurrentSkipListSet

ConcurrentSkipListMap其实是TreeMap的并发版本。TreeMap使用的是红黑树,并且按照key的顺序排序(自然顺序、自定义顺序),但是他是非线程安全的,如果在并发环境下,建议使用ConcurrentHashMap或者ConcurrentSkipListMap。

ConcurrentSkipListSet其实是TreeSet的并发版本。TreeSet底层使用红黑树,并且按照key的顺序排序(自然顺序、自定义顺序),但是他是非线程安全的,如果在并发环境下ConcurrentSkipListSet。

ConcurrentSkipListMap和ConcurrentSkipListSet底层使用跳表数据结构来实现,跳表全称叫做跳跃表,它是一个随机化的数据结构,可以被看做二叉树的一个变种,通过使用“跳跃式”查找的方式使得插入、读取数据时复杂度变成了O(logn)。它在性能上和红黑树,AVL树不相上下,但是跳表的原理非常简单,目前在Redis和LeveIDB中都有用到。

跳跃表是一种典型的“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。实现跳表的基础是保证元素有序。

结构图

跳表.jpg

从图中可以看到, 跳跃表主要由以下部分构成:

  • 表头(head):负责维护跳跃表的节点指针。
  • 跳跃表节点:保存着元素值,以及多个层。
  • 层:保存着指向其他元素的指针。高层的指针越过的元素数量大于等于低层的指针,为了提高查找的效率,程序总是从高层先开始访问,然后随着元素值范围的缩小,慢慢降低层次。
  • 表尾:全部由 NULL 组成,表示跳跃表的末尾。

ConcurrentSkipListMap源码分析

ConcurrentSkipListMap类图

ConcurrentSkipListMap.png

查找节点

我们来看下查找9的流程:
head(4)->4(4)->4(3)->4(2)->7(2)->7(1)->9


跳表找到9.png

ConcurrentSkipListMap.findPredecessor()

找到与key距离最近的一个点,如果没找到返回头结点

    // 找到与key距离最近的一个点,如果没找到返回头结点
    private Node findPredecessor(Object key, Comparator cmp) {
        if (key == null)
            throw new NullPointerException(); // don't postpone errors
        for (;;) {
            // 从头结点开始,q就是要找的节点,r是q同层级的右节点,d是q的下一个层级的节点
            for (Index q = head, r = q.right, d;;) {
                if (r != null) {
                    // n是r的数据节点
                    Node n = r.node;
                    K k = n.key;
                    if (n.value == null) {
                        if (!q.unlink(r))
                            break;           // restart
                        r = q.right;         // reread r
                        continue;
                    }
                    // 比较key的大小,key>k则q指q的右节点,继续下一次循环
                    if (cpr(cmp, key, k) > 0) {
                        q = r;
                        r = r.right;
                        continue;
                    }
                }
                // 表示已经到最底层了
                if ((d = q.down) == null)
                    return q.node;
                // 如果r==null || key<=k,则q指向q下一层级的节点
                q = d;
                r = d.right;
            }
        }
    }

ConcurrentSkipListMap.get()

private V doGet(Object key) {
        if (key == null)
            throw new NullPointerException();
        Comparator cmp = comparator;
        outer: for (;;) {
            for (Node b = findPredecessor(key, cmp), n = b.next;;) {
                Object v; int c;
                if (n == null)
                    break outer;
                Node 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) {
                    @SuppressWarnings("unchecked") V vv = (V)v;
                    return vv;
                }
                if (c < 0)
                    break outer;
                b = n;
                n = f;
            }
        }
        return null;
    }

插入结点

明白了查找的原理后,插入、删除就容易理解了。为了保存跳表的有序性,所以分三步:查找合适位置——进行插入/删除——更新跳表指针,维护层级性。


插入节点.png

ConcurrentSkipListMap.doPut()

    private V doPut(K key, V value, boolean onlyIfAbsent) {
        Node z;             // added node
        if (key == null)
            throw new NullPointerException();
        Comparator cmp = comparator;
        outer: for (;;) {
            for (Node b = findPredecessor(key, cmp), n = b.next;;) {
                if (n != null) {
                    Object v; int c;
                    Node 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) {
                        b = 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(key, value, n);
                if (!b.casNext(n, z))
                    break;         // restart if lost race to append to b
                break outer;
            }
        }

删除结点

删除节点.png

参考:
https://www.cnblogs.com/ygj0930/p/6543901.html

你可能感兴趣的:(JAVA并发容器-ConcurrentSkipListMap,ConcurrentSkipListSet)