java多线程 ConcurrentSkipListMap源码分析

目录

前言

源码注释

术语

节点定义

构造器

get 查找操作

返回情况

findPredecessor

put 插入操作

第一部分

第二部分

第三部分

假设新建层数没有超过最大层数

假设新建层数超过了最大层数

返回情况

remove 删除操作

findNode

tryReduceLevel

返回情况

marker存在的必要性

如果marker不存在

marker存在时

总结


注意:本文转自  https://blog.csdn.net/anlian523/article/details/107123092

前言

ConcurrentSkipListMap是一个可以在高并发环境下执行的有序map容器,在单线程环境下我们应使用TreeMap,在低并发环境下我们可以使用Collections.synchronizedSortedMap包装TreeMap来得到一个线程安全的有序map。ConcurrentSkipListMap底层实现是一个SkipList跳表,简单的说就是一个稍微复杂一点的链表结构。

 

 

源码注释

     * Head nodes          Index nodes
     * +-+    right        +-+                      +-+
     * |2|---------------->| |--------------------->| |->null
     * +-+                 +-+                      +-+
     *  | down              |                        |
     *  v                   v                        v
     * +-+            +-+  +-+       +-+            +-+       +-+
     * |1|----------->| |->| |------>| |----------->| |------>| |->null
     * +-+            +-+  +-+       +-+            +-+       +-+
     *  v              |    |         |              |         |
     * Nodes  next     v    v         v              v         v
     * +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+
     * | |->|A|->|B|->|C|->|D|->|E|->|F|->|G|->|H|->|I|->|J|->|K|->null
     * +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+

上面这段注释已经给出了跳表的基本样子,最底层是base层用来存储数据,其他层都是index层用来加快搜索。可见不管是base层还是index层,第一个节点实际上是不存储真实数据的,而是作为一个dummy节点存在。真正的节点只会在第二个节点开始存储。

这些链表中的基本思想是在删除时标记已删除节点的next指针,以避免与并发插入发生冲突,并且在遍历以跟踪三元组(前驱,节点,后继)时进行标记,以检测何时以及如何取消链接 这些已删除的节点。——可能看完还是有点不懂,我们继续。

删除节点的具体过程如下:

假设在base层中间有这么一段节点,我们想要删除n:

     *        +------+       +------+      +------+
     *   ...  |   b  |------>|   n  |----->|   f  | ...
     *        +------+       +------+      +------+

将n的value置为null,代表它被标记为删除节点了。从此刻开始,后续的public操作都不会遍历经过n节点了,但正在进行中的插入和删除操作(它们没有检测到n的value为null),可能会去尝试修改n的后继。
在n和f之间插入一个marker,这个marker的key为null,value指向自己。从此刻开始,任何节点都不可能添加到n后面了。
 

     *        +------+       +------+      +------+       +------+
     *   ...  |   b  |------>|   n  |----->|marker|------>|   f  | ...
     *        +------+       +------+      +------+       +------+

将b的next指向f,从此刻开始,任何遍历都不可能遇到n了,n将会被gc回收掉。
 

     *        +------+                                    +------+
     *   ...  |   b  |----------------------------------->|   f  | ...
     *        +------+                                    +------+

一个节点的删除过程必须经过这三个步骤,而且必须以这个顺序执行,这就是删除节点过程的“状态机”。

术语

base层:最下面的层(第0层),节点类型为Node。
index层:第1层或以上,节点类型为Index。
node节点:base层的节点。
index节点:index层的节点。
headIndex节点:index层的头节点。
marker:不存储数据,只是为了删除时使用。
被标记为删除:Node的value为置为null。简称为“被标记”。

节点定义

static final class Node<K,V> {
    final K key;
    volatile Object value;
    volatile Node<K,V> next;

    Node(K key, Object value, Node<K,V> next) {//正常构造node
        this.key = key;
        this.value = value;
        this.next = next;
    }
    Node(Node<K,V> next) {//构造marker
        this.key = null;
        this.value = this;
        this.next = next;
    }
}

这个是base层的node节点的定义,是真正存储数据的层,也是跳表的最底层。node节点只有right指针。

static class Index<K,V> {
    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;
    }
}

这个是index层的index节点的定义,用来跳跃寻找目标。index节点有right和down指针。

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节点里也有数字,但这只是为了方便看图,index其实并没有直接存储数据。如果单独拎出来一列的话,它是这样的:

构造器

//不指定Compartor的话,则key肯定是Comparable子类对象
public ConcurrentSkipListMap() {
    this.comparator = null;
    initialize();
}
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
    initialize();
}

private void initialize() {
    keySet = null;
    entrySet = null;
    values = null;
    descendingMap = null;
// private static final Object BASE_HEADER = new Object();
// 下面new的node节点,作为base层的dummy节点存在
    head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
                              null, null, 1);
}

可见初始的时候,只有base层和一层index层,而且这两层都只有一个dummy节点作为头节点。

get 查找操作

    public V get(Object key) {
        return doGet(key);
    }
    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;;) {//b作为被找到的前驱
                Object v; int c;
                if (n == null)//b的后继为null,说明寻找目标不存在
                    break outer;
                Node<K,V> f = n.next;
                // 此时三个node的关系应该是b--->n--->f
                if (n != b.next)  // 如果n已经不是b的后继,从头开始循环以执行单次表达式
                    break;
                if ((v = n.value) == null) {  // n的value为null代表n被删除
                    n.helpDelete(b, f);   //进行base层的删除活动
                    break;
                }
                if (b.value == null || v == n)  //如果b被标记或n是个marker,说明b被删除
                    break;
                if ((c = cpr(cmp, key, n.key)) == 0) {//说明目标为找到,就是n
                    @SuppressWarnings("unchecked") V vv = (V)v;
                    return vv;
                }
                if (c < 0)//说明目标不存在,因为 b <目标 < n
                    break outer;
                //if (c 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值