【JUC】JDK1.8源码分析之ConcurrentSkipListSet(八)

简介: 分析完了CopyOnWriteArraySet后,继续分析Set集合在JUC框架下的另一个集合,ConcurrentSkipListSet,ConcurrentSkipListSet一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。set 的元素可以根据它们的自然顺序进行排序,也可以根据创建 set 时所提供的 Comparator 进行排序,具体取决于使用的构造方法。

一、前言


  分析完了CopyOnWriteArraySet后,继续分析Set集合在JUC框架下的另一个集合,ConcurrentSkipListSet,ConcurrentSkipListSet一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。set 的元素可以根据它们的自然顺序进行排序,也可以根据创建 set 时所提供的 Comparator 进行排序,具体取决于使用的构造方法。


二、ConcurrentSkipListSet的数据结构


  由于ConcurrentSkipListSet是基于ConcurrentSkipListMap的实现,所以,其底层数据结构与ConcurrentSkipListMap的相同,具体可以参考ConcurrentSkipListMap,其数据结构如下。

download.png

 说明:ConcurrentSkipListSet将所有键(key)所对应的值(value)均为Boolean.TRUE,所以数据结构与ConcurrentSkipListMap完全相同,并且对ConcurrentSkipListSet的操作都会转化为对ConcurrentSkipListMap的操作。


三、ConcurrentSkipListSet源码分析


 3.1 类的继承关系  

public class ConcurrentSkipListSet<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable {}

说明:ConcurrentSkipListSet继承了AbstractSet抽象类,AbstractSet提供 Set 接口的骨干实现,从而最大限度地减少了实现此接口所需的工作;同时实现了NavigableSet接口,NavigableSet具有了为给定搜索目标报告最接近匹配项的导航方法;同时实现了额Serializable接口,表示可以被序列化。


  3.2 类的属性 

public class ConcurrentSkipListSet<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable {
    // 版本序列号
    private static final long serialVersionUID = -2479143111061671589L;
    // 对ConcurrentSkipListSet提供支持
    private final ConcurrentNavigableMap<E,Object> m;
    // 反射机制
    private static final sun.misc.Unsafe UNSAFE;
    // m字段的内存偏移量
    private static final long mapOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = ConcurrentSkipListSet.class;
            mapOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("m"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

  说明:可以看到ConcurrentSkipListSet有一个ConcurrentNavigableMap字段m,表示一个接口类型,ConcurrentSkipListMap类实现了该接口,也正是ConcurrentNavigableMap对ConcurrentSkipListSet提供了支持。


  3.3 类的构造函数


  1. ConcurrentSkipListSet()型构造函数 

 public ConcurrentSkipListSet() {
        // 初始化m字段
        m = new ConcurrentSkipListMap<E,Object>();
    }

 说明:该构造函数用于构造一个新的空 set,该 set 按照元素的自然顺序对其进行排序。


  2. ConcurrentSkipListSet(Comparator<? super E>)型构造函数  

public ConcurrentSkipListSet(Comparator<? super E> comparator) {
        // 初始化m字段,带有构造器
        m = new ConcurrentSkipListMap<E,Object>(comparator);
    }

 说明:该构造函数用于构造一个新的空 set,该 set 按照指定的比较器对其元素进行排序。


  3. ConcurrentSkipListSet(Collection<? extends E>)型构造函数  

    public ConcurrentSkipListSet(Collection<? extends E> c) {
        // 初始化m字段
        m = new ConcurrentSkipListMap<E,Object>();
        // 添加s集合所有元素
        addAll(c);
    }

 说明:该构造函数用于构造一个包含指定 collection 中元素的新 set,这个新 set 按照元素的自然顺序对其进行排序。


  4. ConcurrentSkipListSet(SortedSet<E>)型构造函数 

 public ConcurrentSkipListSet(SortedSet<E> s) {
        // 初始化m字段
        m = new ConcurrentSkipListMap<E,Object>(s.comparator());
        // 添加s集合所有元素
        addAll(s);
    }

  说明:该构造函数用于构造一个新 set,该 set 所包含的元素与指定的有序 set 包含的元素相同,使用的顺序也相同。


  5. ConcurrentSkipListSet(ConcurrentNavigableMap<E, Object>)型构造函数  

 ConcurrentSkipListSet(ConcurrentNavigableMap<E,Object> m) {
        // 初始化m字段
        this.m = m;
    }

说明:该构造函数不是public的,在外部无法调用,用于生成子集时调用。

  

3.4 核心函数分析


  对ConcurrentSkipListSet的操作(如add、remove、clear等操作)都是基于ConcurrentSkipListMap,所以参考ConcurrentSkipListMap的核心函数分析即可,这里不再累赘。


四、示例


  下面通过一个示例来理解ConcurrentSkipListSet的使用。

package com.hust.grid.leesf.collections;
import java.util.Iterator;
import java.util.concurrent.ConcurrentSkipListSet;
class PutThread extends Thread {
    private ConcurrentSkipListSet<Integer> csls;
    public PutThread(ConcurrentSkipListSet<Integer> csls) {
        this.csls = csls;
    }
    public void run() {
        for (int i = 0; i < 10; i++) {
            csls.add(i);
        }
    }
}
public class ConcurrentSkipListSetDemo {
    public static void main(String[] args) {
        ConcurrentSkipListSet<Integer> csls = new ConcurrentSkipListSet<Integer>();
        PutThread p1 = new PutThread(csls);
        PutThread p2 = new PutThread(csls);
        p1.start();
        p2.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Iterator<Integer> iterator = csls.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }        
    }
}

运行结果(某一次) 

0 1 2 3 4 5 6 7 8 9 

说明:有两个PutThread线程向ConcurrentSkipListSet中添加元素,并且添加的元素是相同的,之后通过迭代器访问ConcurrentSkipListSet,发现元素只会出现一次,并且,值得注意的是,迭代器是弱一致的,返回的元素将反映迭代器创建时或创建后某一时刻的映射状态。它们不抛出 ConcurrentModificationException,可以并发处理其他操作。


五、总结


  ConcurrentSkipListSet是基于ConcurrentSkipListMap实现的,并且迭代器是弱一致性的,即在迭代的过程中,可以有其他修改ConcurrentSkipListSet的操作,不会抛出ConcurrentModificationException异常,在分析了ConcurrentSkipListMap后,再分析ConcurrentSkipListSet就变得十分简单了。谢谢各位园友的观看~

目录
相关文章
|
4月前
|
存储 安全 Java
【JDK 源码分析】ConcurrentHashMap 底层结构
【1月更文挑战第27天】【JDK 源码分析】ConcurrentHashMap 底层结构
|
4月前
|
Java
【JDK 源码分析】HashMap 操作方法
【1月更文挑战第27天】【JDK 源码分析】HashMap Put 元素 初始化
|
4月前
|
存储 网络协议 Java
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)(二)
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)
60 0
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)(二)
|
4月前
|
Java
Integer类【JDK源码分析】
Integer类【JDK源码分析】
38 0
|
10月前
源码分析系列教程(12) - 手写Map框架(基于JDK1.7)
源码分析系列教程(12) - 手写Map框架(基于JDK1.7)
31 0
|
3月前
|
存储 算法 安全
JDK源码分析-HashMap
JDK源码分析-HashMap
|
算法 搜索推荐 Java
Arrays类【JDK源码分析】
Arrays类【JDK源码分析】
45 0
|
安全 Java API
StringBuilder类【JDK源码分析】
StringBuilder类【JDK源码分析】
75 0
|
4月前
|
存储 数据库
享元模式、在 JDK-Interger 的应用源码分析
享元模式(案例解析)、在 JDK-Interger 的应用源码分析
|
Java API 索引
LinkedList类【JDK源码分析】
LinkedList类【JDK源码分析】
48 0