Java常用并发容器总结(四)

2020-09-03 10:30:34 浏览数 (1)

ConcurrentSkipListMap

1.介绍

跳表是一种可以用来快速查找的数据结构,类似于平衡树,他们都可以对元素进行快速的查找。但一个重要的区别是:对平衡树的插入往往可能导致平衡树进行一次全局的调整;而对跳表的插入和删除之需要的局部数据操作即可。这样的好处是,在高并发环境下,对平衡树的操作需要一个全局锁来保证线程安全,但是对于跳表则只需要部分锁,这样会拥有更好地性能。 就查询的性能而言,跳表的时间复杂度也是O(logN)。因此,在并发的数据结构中,JDK使用跳表实现一个Map。 跳表的本质是分层的多个链表,最上层的元素最少,查找操作从最上层开始进行。 使用跳表实现Map和使用Hash算法实现Map的另一个不同之处在于,HashMap并不会维护元素的顺序,而跳表内的所有元素都是排序的,在对跳表遍历时会得到一个有序的结果。因此,如果需要一个有序的并发安全的Map,使用跳表是不二的选择。

2.代码分析

ConcurrentSkipListMap是使用跳表实现的并发安全的Map结构,其内部由几个关键的数据结构组成。首先是Node。一个Node表示跳表的某一层的一个节点。

代码语言:javascript复制
 //一个Node表示跳表的一个节点
 static final class Node<K,V> {
        //该节点的key
        final K key;

        //该节点的value
        volatile Object value;

        //数据域,指向同一层的后继节点
        volatile Node<K,V> next;

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

另一个重要的数据结构是Index,它内部包装了Node,同时增加了向下的引用和向右的引用:

代码语言:javascript复制
 static class Index<K,V> {
        //内部封装了一个Node
        final Node<K,V> node;

        //数据域,指向下一层的节点
        final Index<K,V> down;

        //数据域,指向同一层的右边的节点
        volatile 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;
        }

此外,还有一个HeadIndex的数据结构,表示链表头部的第一个Index,并记录当前在第几层:

代码语言:javascript复制
 //HeadIndex表示第level层的表头Index
 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) {
            super(node, down, right);
            this.level = level;
        }
    }

对于跳表的所有操作,就是组织好这些Index之间的连接关系。

3.适用场景

如果想在高并发环境下,使用一个有序的Map,那么ConcurrentSkipListMap将是不二的选择。

0 人点赞