一、前言
分析完了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); } } }
说明:可以看到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就变得十分简单了。谢谢各位园友的观看~