Java8 ConcurrentSkipListMap与ConcurrentSkipListSet (一) 源码解析

本文详细探讨了Java8中基于SkipList实现的线程安全集合类——ConcurrentSkipListSet和ConcurrentSkipListMap。文章从它们的定义、数据结构、节点类型、跳跃表原理、构造方法到核心操作如put、remove等的实现进行了深入解析,揭示了这两个类如何实现高效并发操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     目录

一、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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值