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

一、前言

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

二、ConcurrentSkipListSet的数据结构

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

  

  说明: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);
        }
    }
}
View Code

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

  3.3 类的构造函数

  1. ConcurrentSkipListSet()型构造函数 

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

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

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

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

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

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

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

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

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

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

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

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

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

  说明:该构造函数不是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() + " ");
        }        
    }
}
View Code

  运行结果(某一次) 

0 1 2 3 4 5 6 7 8 9 

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

五、总结

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

posted @ 2016-06-01 15:38  leesf  阅读(1813)  评论(0编辑  收藏  举报