ConcurrentSkipListMap

基于跳表的并发安全map

ConcurrentSkipListMap以跳表为基础结构的map集合,并且同时支持并发操作,比ConcurrentHashMap最大的优势应该就是有序
在这里插入图片描述
内部结构
在这里插入图片描述
ConcurrentSkipListMap 实现了ConcurrentNavigableMap 接口继承于NavigableMap ,NavigableMap 则继承于sortedMap接口SortedMap提供了一些根据键范围进行查找的功能,比如返回整个Map中 key最小/大的键、返回某个范围内的子Map视图等等。

NavigableMap 接口进一步扩展了SortedMap的功能,提供了根据指定Key返回最接近项、按升序/降序返回所有键的视图等功能
在这里插入图片描述
属性

@SuppressWarnings("serial") // Conditionally serializable
final Comparator<? super K> comparator;

/** 懒加载跳表最上层的节点 */
private transient Index<K,V> head;
/** 原子累加器计数元素的个数*/
private transient LongAdder adder;
/**为subSet headSet 等方法提供的集合,返回某一范围的跳表key构成的集合 */
private transient KeySet<K,V> keySet;
/**返回map的value构成的集合 */
private transient Values<K,V> values;
/**返回key-value 键值对的集合 */
private transient EntrySet<K,V> entrySet;
/** 返回key 值递减的map集合 */
private transient SubMap<K,V> descendingMap;

内部结构

ConcurrentSkipListMap 在jdk11的时候删除了HeadIndex这种类型

ConcurrentSkipListMap内部一共定义了2 种不同类型的结点,元素的增删改查都从最上层的head指针指向的结点开始:

普通的数据节点node节点保存key value , next 指针会形成单链表

 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;
    }
}

很清楚的看到起内部结构
在这里插入图片描述
jdk 文档给的样例

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
     * +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+
  • 表头(head):负责维护跳跃表的节点指针。
  • 普通的node 是index nodes 里属性,node是个局部单链表,这点和我们自定义的跳表略有差异
  • index nodes的right 指针指向同级的index nodes
  • down 指针指向下一层级的index nodes
  • 每一层的indexNode的node基础节点均指向完整链表的某个位置,最底层维护者一个完整的单链表
  • 用Node来保存数据并组成完整数据的链表,并且这个链表相对独立,并不参与构建跳表结构。
  • 从高层先开始访问,然后随着元素值范围的缩小,慢慢降低层次。
  • 表尾:全部由 NULL 组成,表示跳跃表的末尾。

构造器

public ConcurrentSkipListMap() {
    this.comparator = null;
    
}
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
 
}
public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
    this.comparator = null;
    putAll(m);
}
public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
    this.comparator = m.comparator();
    buildFromSorted(m);
}

put

public V put(K key, V value) {
        if (value == null)//value 不能为空
            throw new NullPointerException();
        return doPut(key, value, false);
}

private V doPut(K key, V value, boolean onlyIfAbsent) {
        if (key == null)// 感觉多余的,为啥不放在put里判断
            throw new NullPointerException();
        Comparator<? super K> cmp = comparator;
        for (;;) {//自旋,cas失败的线程仍然可以重新尝试
            Index<K,V> h; Node<K,V> b;
            //ConcurrentSkipListMap 没有使用volatile 关键字,使用varhandel句柄保证内存可见性,禁止指令重排
            //加写屏障,保证写屏障之前的指令不会重排到写屏障之后
            VarHandle.acquireFence();
            int levels = 0;                    // 初始层级为0
            if ((h = head) == null) {          // 实例化head节点
                Node<K,V> base = new Node<K,V>(null, null, null);
                h = new Index<K,V>(base, null, null);//设置索引层级的头结点
                b = (HEAD.compareAndSet(this, null, h)) ? base : null;
            }
            else {
                for (Index<K,V> q = h, r, d;;) { // //每一层级都需要插入q  从上之下开始
                    while ((r = q.right) != null) {//index node存在右节点
                        Node<K,V> p; K k;
                         //如果当前p节点已经被其他线程给删除了,说明r indexNode不可以用了,用r的下一个indexNode替换
                        if ((p = r.node) == null || (k = p.key) == null ||
                            p.val == null)
                            RIGHT.compareAndSet(q, r, r.right);
                        //如果带插入的key,比当前index node 的node(node 节点是个链表的头节点后面的next节点已经排好序的直接比较头结点即可) 节点key值大。说明还要继续向右查找 
                        else if (cpr(cmp, key, k) > 0)
                            q = r; //保存引用
                        else
                            break;//小于或者等于需要从下一level插入
                    }
                    if ((d = q.down) != null) {//当前层级的right =null了需要从down 层级下插入
                        ++levels;
                        q = d;
                    }
                    else {
                        b = q.node;//找到了需要插入的node 的位置
                        break;
                    }
                }
            }
            if (b != null) {//插入到node节点的单链表中
                Node<K,V> z = null;              // new node, if inserted
                for (;;) {                       // find insertion point
                    Node<K,V> n, p; K k; V v; int c;
                    if ((n = b.next) == null) {//说明已经到该层级的尾部了
                        if (b.key == null)       // if empty, type check key now
                            cpr(cmp, key, key);
                        c = -1;
                    }
                    //既然是并发容器需要考虑key被删除的情况,判断node链表第二个节点是否为空
                    else if ((k = n.key) == null)
                        break;                   // can't append; restart
                    else if ((v = n.val) == null) {//既然是并发容器需要考虑value被删除的情况
                        unlinkNode(b, n);//从链表移除n节点,b是前驱节点
                        c = 1;
                    }
                    else if ((c = cpr(cmp, key, k)) > 0)//如果当前key大继续沿着链表找
                        b = n;
                    //如果给定的值插入位置的key值相等则替换后续节点value,并返回后续节点的原值
                    //修改已存在的key的时候,是不会进行层数变化的
                    else if (c == 0 &&
                             (onlyIfAbsent || VAL.compareAndSet(n, v, value)))
                        return v;
                   //小于则插入到该位置的前面
                    if (c < 0 &&
                        NEXT.compareAndSet(b, n,
                                           p = new Node<K,V>(key, value, n))) {
                        z = p;
                        break;
                    }
                }
                //ConcurrentSkipListMap的分层数是通过一个随机数生成算法来确定,判断是否需要增加层级,如果需要就在各层级中插入对应的Index结点。
               /*
               当该随机数的二进制最高位与最末位不为0的时候,put进该Map的数据只会在最底层链表中,不会在高层链表中构建节点。当该随机数的二进制最高位与最末位都为0的时候,且该随机数从二进制第二位开始向左有多少个1,就代表会在多少层高层链表中构建节点,当然超过原跳表的最高层只会增加一层。
               */
                if (z != null) {
                    int lr = ThreadLocalRandom.nextSecondarySeed();//生成低位随机数
                    //0x是十六进制的前缀 0x3:3先转成十进制为3得到的再转成二进制0011即lr&0011 为true表示需要增加层级
                   if ((lr & 0x3) == 0) {
                        int hr = ThreadLocalRandom.nextSecondarySeed();//生成高位随机数    
                        long rnd = ((long)hr << 32) | ((long)lr & 0xffffffffL);
                        int skips = levels;      // levels to descend before add
                        Index<K,V> x = null;
                        for (;;) {               // create at most 62 indices
                            x = new Index<K,V>(z, x, null);
                            if (rnd >= 0L || --skips < 0)
                                break;
                            else
                                rnd <<= 1;
                        }
                        if (addIndices(h, skips, x, cmp) && skips < 0 &&
                            head == h) {         // try to add new level
                            Index<K,V> hx = new Index<K,V>(z, x, null);
                            Index<K,V> nh = new Index<K,V>(h.node, h, hx);
                            HEAD.compareAndSet(this, h, nh);
                        }
                        if (z.val == null)       // deleted while adding indices
                            findPredecessor(key, cmp); // clean
                    }
                    addCount(1L);//计数+1
                    return null;
                }
            }
        }
    }

get

查找的时候从最顶层的head节点开始,key值和head 的node节点的第一个key进行比较(仅比较第一个即可,后面的均是排好序的),如果key值大则继续从当前级别向右查找,反之小则需要从当前node节点向下一级别查找,直至遍历到最底层的链表为止。

public V get(Object key) {
        return doGet(key);
    }

private V doGet(Object key) {
        Index<K,V> q;
        VarHandle.acquireFence();//读屏障,禁止指令重排
        if (key == null)
            throw new NullPointerException();
        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;
                    //如果当前p节点已经被其他线程给删除了,说明r indexNode不可以用了,用r的下一个indexNode替换
                 if ((p = r.node) == null || (k = p.key) == null ||
                        (v = p.val) == null)
                        RIGHT.compareAndSet(q, r, r.right);
                  else if ((c = cpr(cmp, key, k)) > 0)//查找的key比当前k大,需要继续查找 
                        q = r;
                  else if (c == 0) {//相等找到直接返回value
                        result = v;
                        break outer;
                  }
                  else
                        break;//小于的后面不用比较了需要从下一个级别查找
                }
                if ((d = q.down) != null)//从下一级别level查找
                    q = d;
                else {
                    //到这里说明q.down = null,到最底层了即level=1级别,沿着node链表继续查找,最底层没有那就真找不到了
                    Node<K,V> b, n;
                    if ((b = q.node) != null) {
                        while ((n = b.next) != null) {
                            V v; int c;
                            K k = n.key;
                            if ((v = n.val) == null || k == null ||
                                (c = cpr(cmp, key, k)) > 0)
                                b = n;
                            else {
                                if (c == 0)
                                    result = v;
                                break;
                            }
                        }
                    }
                    break;
                }
            }
        }
        return result;
    }

remove

每个Index都保存一个Node,每个竖向的链表都指向底层链表中同一个node引用,删除方法只要把node中的value设置为null,当其他线程遍历到持有这个Node的Index时发现value为null,就移除Index,以这种**“懒删除”**的方式进行,以提高并发效率

 public V remove(Object key) {
        return doRemove(key, null);
    }

final V doRemove(Object key, Object value) {
        if (key == null)
            throw new NullPointerException();
        Comparator<? super K> cmp = comparator;
        V result = null;
        Node<K,V> b;
       // b指向“小于且最接近给定key”的Node结点(或底层链表头结点)
        outer: while ((b = findPredecessor(key, cmp)) != null &&
                      result == null) {
            for (;;) {
                Node<K,V> n; K k; V v; int c;
                if ((n = b.next) == null)
                    break outer;
                else if ((k = n.key) == null) // n is deleted
                    break;
                else if ((v = n.val) == null) //当前线程发现 n 的value为空则删除n
                    unlinkNode(b, n);//从链表删除n
                else if ((c = cpr(cmp, key, k)) > 0)
                    b = n;
                else if (c < 0)
                    break outer;
                 // 此时n指向查到的结点,value已经被修改,直接跳出
                else if (value != null && !value.equals(v))
                    break outer;
                //删除关键:把node n中的value设置为null,当其他线程遍历到持有这个Node的Index时发现value为null,就移除Index,以这种“懒删除”的方式进行,以提高并发效率
                else if (VAL.compareAndSet(n, v, null)) {
                    result = v;
                    unlinkNode(b, n);//删除n结点
                    break; // loop to clean up
                }
            }
        }
        if (result != null) {
            tryReduceLevel();//减少层级
            addCount(-1L);//size -1
        }
        return result;
    }
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值