Java8 ConcurrentSkipListMap与ConcurrentSkipListSet (一) 源码解析

     目录

一、ConcurrentSkipListSet

二、ConcurrentSkipListMap

1、定义

2、Node / Index / HeadIndex

3、SkipList 跳跃表

4、构造方法

5、put / putIfAbsent 

6、remove / replace / replaceAll / clear

7、get / getOrDefault


      ConcurrentSkipListSet是基于ConcurrentSkipListMap实现的线程安全的NavigableSet接口实现类,与之对应的线程不安全的实现类就是基于TreeMap实现的TreeSet;ConcurrentNavigableMap是基于SkipList跳跃表实现的线程安全的NavigableMap实现类,后者对应的线程不安全的实现类就是基于红黑树实现的TreeMap。本篇博客就详细探讨这两类的实现细节。

一、ConcurrentSkipListSet

      ConcurrentSkipListSet的类继承关系如下:

       

     其实现的核心接口NavigableSet的另一个典型实现类就是TreeSet,跟TreeSet基于TreeMap实现一样, ConcurrentSkipListSet的实现基于ConcurrentSkipListMap的,添加和删除元素,元素遍历等都是基于ConcurrentSkipListMap方法的,value固定为Boolean.TRUE,其常用方法实现如下:

public ConcurrentSkipListSet() {
        m = new ConcurrentSkipListMap<E,Object>();
    }

public ConcurrentSkipListSet(Comparator<? super E> comparator) {
        m = new ConcurrentSkipListMap<E,Object>(comparator);
    }


public boolean add(E e) {
        return m.putIfAbsent(e, Boolean.TRUE) == null;
    }

public boolean remove(Object o) {
        return m.remove(o, Boolean.TRUE);
    }

public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }

public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }

二、ConcurrentSkipListMap

1、定义

     ConcurrentSkipListMap的类继承关系如下:

     

     从上图可知,ConcurrentNavigableMap继承自ConcurrentMap和NavigableMap,后者的典型实现类就是TreeMap,纯粹是基于红黑树实现的,可以参考《java8 TreeMap接口实现源码解析》。ConcurrentNavigableMap没有添加新的方法,只是改写了父类接口的方法定义,如下:

这些方法的用途可以参考API,会挑选其中典型的方法研究其实现细节。该类包含的属性如下:

    //头索引节点
    private transient volatile HeadIndex<K,V> head;

    //比较大小的比较器
    final Comparator<? super K> comparator;
  
    //key视图,下面这些视图都是在请求相关方法时才会创建
    private transient KeySet<K> keySet;
    //键值对视图
    private transient EntrySet<K,V> entrySet;
    //value视图
    private transient Values<V> values;
    //倒序遍历的视图
    private transient ConcurrentNavigableMap<K,V> descendingMap;

 包含的静态常量如下:

//初始化head属性时使用,表示一个空节点
private static final Object BASE_HEADER = new Object();

private static final int EQ = 1;
private static final int LT = 2;
private static final int GT = 0; // Actually checked as !LT

包含的静态属性通过static代码块初始化,如下:

2、Node / Index / HeadIndex

     这三个都是内部类,其中Node表示一个键值对,其定义如下:

static final class Node<K,V> {
        //键值对的key和value
        final K key;
        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;
        }

        Node(Node<K,V> next) {
            this.key = null;
            this.value = this;
            this.next = next;
        }
        
        //cas修改value
        boolean casValue(Object cmp, Object val) {
            return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val);
        }
        
        //cas修改next
        boolean casNext(Node<K,V> cmp, Node<K,V> val) {
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

        boolean isMarker() {
            return value == this;
        }

        boolean isBaseHeader() {
            return value == BASE_HEADER;
        }
        
        //插入一个value指向自己的节点,next为f的节点
        //删除某个节点时会将其value置为null,然后调用此方法在该节点后插入一个marker,将该节点从next链表中移除
        //marker在这里是打一个标识同时保存下一个节点的引用,保证将其从next链表中移除失败,即cas修改前一个节点的next属性失败的情形下该节点可以被正常移除
        boolean appendMarker(Node<K,V> f) {
            return casNext(f, new Node<K,V>(f));
        }
        
        //在查询或者修改节点时,发现某个节点的value已经是null了,会调用此方法
        //b是前一个节点,f是next节点
        void helpDelete(Node<K,V> b, Node<K,V> f) {
            if (f == next && this == b.next) {
                //再次检查链表关系
                if (f == null || f.value != f) 
                    //该节点未插入marker节点,此处重新插入,下次调用时进入else分支
                    casNext(f, new Node<K,V>(f));
                else
                    //f是一个marker节点
                    //将this和f都从链表中移除
                    b.casNext(this, f.next);
            }
        }

       
        V getValidValue() {
            Object v = value;
            if (v == this || v == BASE_HEADER) //value是无效
                return null;
            @SuppressWarnings("unchecked") V vv = (V)v;
            return vv;
        }

        
        AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
            Object v = value;
            if (v == null || v == this || v == BASE_HEADER) //value是无效
                return null;
            @SuppressWarnings("unchecked") V vv = (V)v;
            return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
        }

        //获取属性偏移量
        private static final sun.misc.Unsafe UNSAFE;
        private static final long valueOffset;
        private static final long nextOffset;

        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = Node.class;
                valueOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("value"));
                nextOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

   Index是HeadIndex的父类,都表示SkipList中的索引节点,其实现如下:

static class Index<K,V> {
        //关联的node节点
        final Node<K,V> node; 
        //down指向的节点的node和当前节点的node是同一个,但是down节点位于下一个层级,层级越往下包含的节点数越多,最底层包含了所有的node节点
        final Index<K,V> down; 
        //right指向的节点的key大于当前节点,跟当前节点位于同一个层级,沿着right属性构成的链表遍历,node的key值越来越大
        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;
        }
        
        //cas修改right属性
        final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
            return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val);
        }

        final boolean indexesDeletedNode() {
            return node.value == null;
        }
        
        //将新节点插入到当前节点的rigth链表中,原right节点的前面  
        final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
            Node<K,V> n = node;
            newSucc.right = succ;
            //node的vlue不为空时,将right由succ修改成newSucc
            return n.value != null && casRight(succ, newSucc);
        }

        //将succ从right链表中移除
        final boolean unlink(Index<K,V> succ) {
            //node的vlue不为空时,将right由succ修改成succ.right
            return node.value != null && casRight(succ, succ.right);
        }

        //获取right属性偏移量
        private static final sun.misc.Unsafe UNSAFE;
        private static final long rightOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = Index.class;
                rightOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("right"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

    
    static final class HeadIndex<K,V> extends Index<K,V> {
        //level记录层级
        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;
        }
    }

3、SkipList 跳跃表

       跳跃表是一种改进自链表的数据结构,用于存储有序的数据,通过空间换时间的方法来提高数据检索的速度。 普通的链表或者数组插入和删除的效率都是O(1),但是查找的效率是O(n),如果是有序链表或者有序数组,则可以通过二分法快速查找,复杂度是O(logn)。跳跃表在理想情况下是O(logn),最差的情况下是O(n),因为跳跃表“跳跃”的下一个节点是完全随机的。其数据结构如下图:

     如图所示,跳跃表是有多个层级的,每个层级都是一个有序的链表,越往上的层级节点数越少,最底层则包含了所有的节点,每一层的节点的起始节点都是head节点。      

     如图所示,跳跃表的节点有两种,一种是最底层的,层级是0的节点,跟普通链表的节点一样,通过next属性构成链表,就是上面的Node类;一种是索引节点,就是上面的Index类,索引节点包含对Node的引用,除此之外还有两个属性,down属性保存下一层级节点的引用,即图中向下的箭头引用,right属性保存同一层级中比当前节点指向的Node大的的节点,即图中向右的箭头引用,注意通过down属性链接起来的节点其指向的Node都是同一个且这些节点都是在Node节点创建时根据Node节点的层级一起创建的。还有一种特殊的索引节点,图中的head节点,会额外保存当前跳跃表的层级,即HeadIndex类中的level属性,在插入新节点时会判断新节点的层级是否大于当前head节点的层级,如果是,需要相应的增加一个head节点,另外,元素遍历时也是通过左上角的head节点开始遍历的,先往右查找,如果碰到比自己大的节点,则在下一层级继续往右查找,直到找到最底层的节点,再通过next属性遍历,找到匹配的节点。跳跃的实现相比普通链表增加了索引节点,通过索引节点跳过一些中间节点,从而提高查找的效率,这就是所谓的空间换时间了。

     注意,跳跃表的层级是取决于所有节点的最大层级的,即如果某个节点的层级大于当前跳跃表的层级,会将当前跳跃表的层级加1,同时创建一个新的head节点,新head节点的right属性指向该节点。而新节点所属的层级是随机的,在插入时通过随机算法确定的,50%的概率是0,25%的概率是1,12.5%的概率是2,层级越高的概率越低。

    理解上述逻辑,可以通过如下测试用例Debug:

    @Test
    public void test() throws Exception {
        Random random=new Random();
        Map<Integer,Integer> test=new ConcurrentSkipListMap<>();
       for(int i=0;i<90;i++){
           test.put(random.nextInt(90),1);
       }
        test.put(random.nextInt(100),1);
        test.put(random.nextInt(100),1);
        test.put(random.nextInt(100),1);
        test.put(random.nextInt(100),1);
        for(int key:test.keySet()){
            System.out.println(key);
        }
    }

 在后面的test.put处打断点,然后进入到doPut方法调试,前面插入的90个节点保证跳跃表已经形成了多个层级; 如果想要查看层级非0的节点的插入逻辑,则直接在doPut方法内已经算出随机数的地方打断点。下图是进入doPut方法后,打印的head节点:

从图中可知,通过down属性链接的多个节点指向同一个Node节点。 

4、构造方法

public ConcurrentSkipListMap() {
        this.comparator = null;
        initialize();
    }

public ConcurrentSkipListMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
        initialize();
    }

public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
        this.comparator = null;
        initialize();
        //使用父类AbstractMap的实现,遍历m中的键值对,通过put方法添加到当前map中
        putAll(m);
    }

public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
        this.comparator = m.comparator();
        initialize();
        buildFromSorted(m);
    }

private void initialize() {
        keySet = null;
        entrySet = null;
        values = null;
        descendingMap = null;
        head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
                                  null, null, 1);
    }

//该方法是在构造方法中调用的,不会被并行访问,所以构建跳跃表的过程中没有使用CAS
private void buildFromSorted(SortedMap<K, ? extends V> map) {
        if (map == null)
            throw new NullPointerException();

        HeadIndex<K,V> h = head;
        Node<K,V> basepred = h.node;

        ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();
        //初始化数组元素的
        for (int i = 0; i <= h.level; ++i)
            preds.add(null);
        
        Index<K,V> q = h;
        //将每一层的head节点保存到preds中,下面的while循环过程中会把同一层级的最右侧的节点保存到preds中
        for (int i = h.level; i > 0; --i) {
            preds.set(i, q);
            q = q.down;
        }

        Iterator<? extends Map.Entry<? extends K, ? extends V>> it =
            map.entrySet().iterator();
        //因为SortedMap中的节点是已经排序好了,所以这里直接构建跳跃表即可,不需要为新节点查找插入位置
        while (it.hasNext()) {
            Map.Entry<? extends K, ? extends V> e = it.next();
            //生成一个随机数
            int rnd = ThreadLocalRandom.current().nextInt();
            int j = 0;
            //计算新节点的层级
            if ((rnd & 0x80000001) == 0) { //如果rnd是一个偶数
                do {
                    ++j;
                } while (((rnd >>>= 1) & 1) != 0); //将rnd右移1位,即除以2,如果结果是偶数就终止循环,否则继续
                //如果大于head的level
                if (j > h.level) j = h.level + 1;
            }
            K k = e.getKey();
            V v = e.getValue();
            if (k == null || v == null)
                throw new NullPointerException();
            Node<K,V> z = new Node<K,V>(k, v, null);
            basepred.next = z;
            basepred = z;
            if (j > 0) {
                Index<K,V> idx = null;
                //从层级1开始往上逐一处理
                for (int i = 1; i <= j; ++i) {
                    idx = new Index<K,V>(z, idx, null);
                    if (i > h.level)
                        //创建一个新的head节点
                        h = new HeadIndex<K,V>(h.node, h, idx, i);

                    if (i < preds.size()) {
                        preds.get(i).right = idx; //index作为上一个节点的right节点
                        preds.set(i, idx); //更新i,即当前节点是这一层级中的最右边的节点
                    } else
                        //i>h.level的情形
                        preds.add(idx);
                }
            }
        }
        //更新head
        head = h;
    }

5、put / putIfAbsent 

        上述两个方法的实现都是doPut方法,该方法的实现整体上可以分为两步,第一步是从最高层级的head索引节点开始往右查找,如果碰到一个比自己大的节点,则进入下一个层级继续往右查找,直到该节点是最底层的索引节点了为止,再根据该索引节点指向的链表节点,通过next属性往后遍历找到一个合适的位置,当前节点大于前面的节点小于后面的节点,然后插入到next链表中。第二步是计算当前节点所属的层级,如果层级不是0,则构建每一层级对应的索引节点,各索引节点通过down属性链接起来且都指向新创建的链表节点,然后从高层级往下遍历,将各索引节点同同一层级的节点建立关联关系,这期间如果新节点的层级高于head节点的层级,需要将当前head节点的层级加1,并创建一个对应的head节点,新的head节点的right属性指向该新节点对应的同一层级的索引节点。

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

public V putIfAbsent(K key, V value) {
        if (value == null)
            throw new NullPointerException();
        return doPut(key, value, true);
    }

private V doPut(K key, V value, boolean onlyIfAbsent) {
        Node<K,V> z;             // added node
        if (key == null)
            throw new NullPointerException();
        Comparator<? super K> cmp = comparator;
        //在next链表中找到合适的位置,将新节点插入进去
        outer: for (;;) {
            //findPredecessor方法返回当前Map中小于key的最大的一个索引节点所指向的节点,且该索引节点位于最底层了,然后遍历通过next属性构成的链表
            //直到找到一个大于key的节点,即下面c小于0的情形,将新节点插入到该节点的前面
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
                if (n != null) {
                    Object v; int c;
                    Node<K,V> f = n.next;
                    if (n != b.next) //next节点变了,终止内层for循环,通过外层的for循环重新获取b和n
                        break;
                    if ((v = n.value) == null) {//n被删除了
                        n.helpDelete(b, f);
                        break;
                    }
                    if (b.value == null || v == n) //b被删除了,下一次findPredecessor会将b从链表中移除
                        break;
                    if ((c = cpr(cmp, key, n.key)) > 0) { //如果key大于n.key,继续遍历通过next属性构成的链表中的下一个节点
                        b = n;
                        n = f;
                        continue;
                    }
                    if (c == 0) {
                        //如果找到key相等的节点,
                        if (onlyIfAbsent || n.casValue(v, value)) {
                            //onlyIfAbsent为true表示不需要修改,为false则修改value,返回原来的value
                            @SuppressWarnings("unchecked") V vv = (V)v;
                            return vv;
                        }
                        break; //如果cas修改失败,则继续for循环尝试修改,直到修改成功为止
                    }
                    //c小于0会进入下面的new Node
                }
                //如果n为null或者c小于0,c小于0时,b.key < key <n.key
                //创建一个新节点,插入到b和n的中间
                z = new Node<K,V>(key, value, n);
                if (!b.casNext(n, z))
                    break;         //如果修改失败则重试
                break outer;  //终止外层的for循环
            } //内层for循环
        }//外层for循环

        int rnd = ThreadLocalRandom.nextSecondarySeed();
        if ((rnd & 0x80000001) == 0) { //如果是偶数
            //level表示新节点的层级,从1开始增加,0表示最底层的一个层架
            int level = 1, max;
            while (((rnd >>>= 1) & 1) != 0) //将rnd除以2以后,如果是奇数level加1
                ++level;
            //idx表示当前节点通过down属性构成的一个链表,所有节点都指向新节点z    
            Index<K,V> idx = null;
            HeadIndex<K,V> h = head;
            if (level <= (max = h.level)) { //如果level小于head的level,则创建一个通过down属性构成的Index链表
                for (int i = 1; i <= level; ++i)
                    //idx是作为新节点的down节点的
                    idx = new Index<K,V>(z, idx, null);
            }
            else { //如果level大于head的level,需要增加head节点的层级
                level = max + 1; //比head大的话,只能在原来的基础上加1
                //创建一个Index数组
                @SuppressWarnings("unchecked")Index<K,V>[] idxs =
                    (Index<K,V>[])new Index<?,?>[level+1]; //加1是为了加上底层0对应的Index
                //初始化数组元素
                for (int i = 1; i <= level; ++i)
                    idxs[i] = idx = new Index<K,V>(z, idx, null);
                for (;;) {
                    h = head;
                    int oldLevel = h.level;
                    if (level <= oldLevel) //初始状态下level肯定大于oldLevel
                        break;
                    HeadIndex<K,V> newh = h;
                    Node<K,V> oldbase = h.node;
                    //创建新层级对应的head节点,down节点指向原来的head,right节点指向新节点z对应的索引节点
                    //通常oldLevel就比level小1,即只创建一个HeadIndex节课
                    for (int j = oldLevel+1; j <= level; ++j)
                        newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
                    if (casHead(h, newh)) { //cas修改head,如果修改失败说明其他线程在修改head,则重新for循环读取head
                        h = newh; //使用最新的head
                        idx = idxs[level = oldLevel]; //此处level等于oldLevel
                        break;
                    }
                }
            }
            //如果新节点z对应的level不是0,则创建对应层级的down链表,如果层级大于head的还需要调整head的层级,创建新的head节点,新层级的head节点的right节点
            //就指向新节点z
            // find insertion points and splice in
            splice: for (int insertionLevel = level;;) {
                int j = h.level; 
                for (Index<K,V> q = h, r = q.right, t = idx;;) {
                    if (q == null || t == null)
                        break splice;
                    if (r != null) {
                        Node<K,V> n = r.node;
                        // compare before deletion check avoids needing recheck
                        int c = cpr(cmp, key, n.key);
                        if (n.value == null) { //r节点是无效的
                            if (!q.unlink(r)) //将r从q的right链表中移除,移除失败重新读取q
                                break;
                            r = q.right;//获取新的right节点
                            continue;
                        }
                        if (c > 0) {//key大于n.key,则继续遍历下一个right
                            q = r;
                            r = r.right;
                            continue;
                        }
                    }
                    //如果c小于等于0,即key小于等于n.key,或者r等于null
                    //初始状态j肯定是大于insertionLevel,后面没for循环一次,j减1,j就会等于insertionLevel
                    if (j == insertionLevel) {
                        if (!q.link(r, t))  //将t插入到q的right链表中,插入到r的前面
                            break; //link失败,重新读取h
                        //link成功    
                        if (t.node.value == null) { //正常不可能为null,极端并发下可能为null,因为此时已经将新节点插入到链表中了,有可能被其他某个线程给删除了
                            findNode(key); //通过findNode将该节点移除,然后终止外层for循环
                            break splice;
                        }
                        //如果相等了,j和insertionLevel都会减1,后面的for循环中两个都会相等,如果不等则只是j减1
                        if (--insertionLevel == 0) //到底层节点了,终止外层for循环
                            break splice;
                    }
                    
                   
                    if (--j >= insertionLevel //j肯定大于等于insertionLevel
                           && j < level) //如果j大于level,说明未遍历到新节点所在的最高层级,小于level了说明上一个层级的right节点处理好了,需要遍历下一个层级
                        t = t.down;
                     //遍历下一个层级的节点    
                    q = q.down;
                    r = q.right;
                } //内层for循环
            }//外层for循环
        }//if结束
        return null;
    }

//从head节点开始遍历,找到小于key的最大的一个索引节点指向的节点
private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
        if (key == null)
            throw new NullPointerException(); // don't postpone errors
        for (;;) {
            //从head开始往后遍历
            for (Index<K,V> q = head, r = q.right, d;;) {
                if (r != null) {
                    Node<K,V> n = r.node;
                    K k = n.key;
                    if (n.value == null) { //r节点即将被删除
                        //将r从q的right链表中移除,如果q的node的value为null或者cas失败都会则返回false
                        if (!q.unlink(r)) 
                            break;      //终止内层for循环,通过外层的for循环重新开始,重新读取head
                        r = q.right;         //如果unlink成功,重新读取r
                        continue;
                    }
                    //n.value不为null
                    if (cpr(cmp, key, k) > 0) {
                        //如果key大于k,继续遍历下一个right
                        q = r;
                        r = r.right;
                        continue;
                    }
                }
                //如果key小于k 或者r等于null
                //如果down节点为null,说明已经到最下面的一层节点了,如果不为null,则继续遍历下一层节点
                //最简单的一个场景,head只有一个层级,即down为null,right不为null,会沿着right属性构成的链表一直遍历直到遇到一个大于key的节点
                //此时q就是小于key的最大的一个节点
                //如果down不为null,则遍历down节点的right,注意down节点和q节点指向的node是同一个,同样的沿着right属性构成的链表一直遍历直到遇到
                //一个大于key的节点,q就是上一个小于key的最大的节点
                if ((d = q.down) == null) 
                    return q.node;
                //如果down不为null,则继续用down的right节点跟key比    
                q = d;
                r = d.right;
            }
        }
    }
//c不为空null,通过c比较大小,否则强转成Comparable比较
 static final int cpr(Comparator c, Object x, Object y) {
        return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y);
    }

private Node<K,V> findNode(Object key) {
        if (key == null)
            throw new NullPointerException(); // don't postpone errors
        Comparator<? super K> cmp = comparator;
        outer: for (;;) {
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
                Object v; int c;
                if (n == null) //没有匹配的节点,终止外层循环,返回null
                    break outer;
                Node<K,V> f = n.next;
                if (n != b.next)  //重新读取b
                    break;
                if ((v = n.value) == null) {//n被删除了
                    n.helpDelete(b, f);
                    break;
                }
                if (b.value == null || v == n)  //b呗删除了
                    break;
                if ((c = cpr(cmp, key, n.key)) == 0)//找到目标节点
                    return n;
                if (c < 0) //没有匹配节点,终止外层循环,返回null
                    break outer;
                //c大于0,继续遍历下一个节点    
                b = n;
                n = f;
            }
        }
        return null;
    }

  private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
        return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
    }

6、remove / replace / replaceAll / clear

        remove方法的核心实现是doRemove,先按照同样的方式遍历找到目标节点,然后将该节点的value置为null,紧接着在该节点后插入一个空的marker节点,如果成功了则将该节点和之前插入对的marker节点一起从链表中移除,插入marker节点的原因就是为了保证后面的从链表移除的动作可以顺利完成,最后再移除该节点对应的索引节点,将其从同一层级的前一个节点的right链表中移除。 最后两步的移除动作,也可以并行的被其他线程执行,他们判断所引用的Node节点的value为null,会自动执行对应的删除动作,所以删除动作都是CAS实现的。

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

public boolean remove(Object key, Object value) {
        if (key == null)
            throw new NullPointerException();
        return value != null && doRemove(key, value) != null;
    }

//将等于key的节点的value替换成指定值,返回替换前的值,相当于put方法
public V replace(K key, V value) {
        if (key == null || value == null)
            throw new NullPointerException();
        for (;;) {
            Node<K,V> n; Object v;
            if ((n = findNode(key)) == null)
                return null; //如果没有找到目标节点
            //校验n.value不为空是为了避免该节点被删除了
            //然后cas修改value    
            if ((v = n.value) != null && n.casValue(v, value)) {
                @SuppressWarnings("unchecked") V vv = (V)v;
                return vv;//返回以前的值
            }
        }
    }

//要求key和value都相等才替换,返回替换是否成功
public boolean replace(K key, V oldValue, V newValue) {
        if (key == null || oldValue == null || newValue == null)
            throw new NullPointerException();
        for (;;) {
            Node<K,V> n; Object v;
            if ((n = findNode(key)) == null)
                return false;
            if ((v = n.value) != null) {
                if (!oldValue.equals(v)) //如果当前值不是oldValue返回false
                    return false;
                if (n.casValue(v, newValue)) //如果cas修改成功,返回true
                    return true;
            }
        }
    }

public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        if (function == null) throw new NullPointerException();
        V v;
        //获取第一个有效节点后,通过next遍历所有的节点
        for (Node<K,V> n = findFirst(); n != null; n = n.next) {
            while ((v = n.getValidValue()) != null) { //获取有效value
                V r = function.apply(n.key, v);
                //返回值为空,抛出异常
                if (r == null) throw new NullPointerException();
                if (n.casValue(v, r)) //cas修改value成功则终止内层while循环,处理下一个节点
                    break;
            }
        }
    }

//获取第一个有效节点,其实就是head.node.next
final Node<K,V> findFirst() {
        for (Node<K,V> b, n;;) {
            if ((n = (b = head.node).next) == null)
                return null; //如果next为null,返回null
            if (n.value != null)
                return n;  
            //如果n.value为null,则删除该节点    
            n.helpDelete(b, n.next);
        }
    }

final V doRemove(Object key, Object value) {
        if (key == null)
            throw new NullPointerException();
        Comparator<? super K> cmp = comparator;
        outer: for (;;) {
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
                Object v; int c;
                if (n == null)  //没有匹配的节点,终止外层for循环,返回null
                    break outer;
                Node<K,V> f = n.next;
                if (n != b.next)  //next节点被修改了,重新读取b
                    break;
                if ((v = n.value) == null) { //n被删除了
                    n.helpDelete(b, f); //删除节点n
                    break;
                }
                if (b.value == null || v == n) // b被删除了,重新读取b
                    break;
                if ((c = cpr(cmp, key, n.key)) < 0) //key小于n.key,不会有匹配的节点了
                    break outer;
                if (c > 0) { //key大于n.key,继续遍历下一个next节点
                    b = n;
                    n = f;
                    continue;
                }
                //c等于0
                if (value != null && !value.equals(v)) //value不为空跟当前节点的value不一致,终止外层for循环,返回null 
                    break outer;
                if (!n.casValue(v, null)) //cas修改失败,内层for循环重试
                    break;
                //casValue成功,在n和f之间插入一个空节点,如果成功则将b的next节点修改成f
                if (!n.appendMarker(f) || !b.casNext(n, f))
                  //value已经置为null,其他线程不可能修改n的next属性,此时只有修改b的next属性会失败
                    findNode(key);   //上述操作有一个失败则执行findNode,借此清除节点和关联的索引节点
                else {
                    //修改成功,通过findPredecessor清理关联的Index节点
                    findPredecessor(key, cmp);      // clean index
                    if (head.right == null) //如果head的right节点为null,则尝试减少层级
                        tryReduceLevel();
                }
                @SuppressWarnings("unchecked") V vv = (V)v;
                return vv;
            }
        }
        return null;
    }

public void clear() {
        for (;;) {
            Node<K,V> b, n;
            HeadIndex<K,V> h = head, d = (HeadIndex<K,V>)h.down;
            if (d != null)
                casHead(h, d);  //修改head,原head的right链表就都被移除了
            //d为null,遍历到最底层的节点了     
            else if ((b = h.node) != null && (n = b.next) != null) {
                Node<K,V> f = n.next;     // remove values
                if (n == b.next) { //b未改变
                    Object v = n.value;
                    if (v == null) //n被删除了
                        n.helpDelete(b, f);
                    //v不为null,将n的value置为null并插入marker,将b的next由n修改成f    
                    else if (n.casValue(v, null) && n.appendMarker(f))
                        b.casNext(n, f); //如果cas成功,则n从链表中移除了,如果失败了,假如此时其他线程在并行的往b后面插入一个节点,则下次遍历到n时通过helpDelete将其从链表中移除
                }
            }
            else
                break;
        }
    }

    private void tryReduceLevel() {
        HeadIndex<K,V> h = head;
        HeadIndex<K,V> d;
        HeadIndex<K,V> e;
        if (h.level > 3 &&
            (d = (HeadIndex<K,V>)h.down) != null &&
            (e = (HeadIndex<K,V>)d.down) != null &&
            e.right == null &&
            d.right == null &&
            h.right == null && //从head往下连续三个层级的right都为null
            casHead(h, d) && //将head从h修改成d
            h.right != null) // 最后一次检查,如果不为null则恢复
            casHead(d, h);   // try to backout
    }

7、get / getOrDefault

      其底层实现doGet方法的实现和findNode方法是基本一致的,如下:

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

public V getOrDefault(Object key, V defaultValue) {
        V v;
        return (v = doGet(key)) == null ? defaultValue : v;
    }

//doGet方法的实现和findNode方法是基本一致的
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;;) {
                Object v; int c;
                if (n == null) //没有匹配节点
                    break outer;
                Node<K,V> f = n.next;
                if (n != b.next)  //重新读取b
                    break;
                if ((v = n.value) == null) {    //n被删除了
                    n.helpDelete(b, f);
                    break;
                }
                if (b.value == null || v == n)  // b被删除了
                    break;
                if ((c = cpr(cmp, key, n.key)) == 0) { //找到目标节点
                    @SuppressWarnings("unchecked") V vv = (V)v;
                    return vv;
                }
                if (c < 0)
                    break outer; 没有匹配节点
                //c大于0,则遍历下一个next节点    
                b = n;
                n = f;
            }
        }
        return null;
    }

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值