concurrentskiplistset 是基于跳表的线程安全有序 set,支持高并发读写与自然/自定义排序;相比 synchronized treeset,它无全局锁、复合操作更安全、迭代弱一致且性能更优。

ConcurrentSkipListSet 是 Java 并发包(java.util.concurrent)中提供的线程安全、有序的 Set 实现,底层基于 ConcurrentSkipListMap,天然支持高并发下的插入、删除、查找操作,且保持元素自然顺序(或自定义比较器顺序)。它不加锁,靠跳表(Skip List)结构实现非阻塞的并发控制,适合读多写少、又要求排序和线程安全的场景。
为什么选 ConcurrentSkipListSet 而不是 TreeSet + Collections.synchronizedSet?
TreeSet 本身不是线程安全的;用 Collections.synchronizedSet(new TreeSet()) 虽能保证单个操作原子性,但复合操作(如“检查不存在再添加”)仍需手动同步,且所有方法串行执行,吞吐量低。而 ConcurrentSkipListSet 的 add、remove、contains 等操作是真正并发的,多个线程可同时在不同层级的跳表节点上工作,无全局锁,性能更优,也天然支持弱一致性迭代(迭代时允许其他线程修改)。
基本用法:声明、添加与遍历
使用方式与普通 Set 类似,但无需额外同步:
- 默认按自然顺序(元素需实现 Comparable):
set.add(5); set.add(1); set.add(3);
// 遍历结果恒为 [1, 3, 5]
- 指定 Comparator 实现自定义排序:
new ConcurrentSkipListSet(String.CASE_INSENSITIVE_ORDER);
caseInsensitiveSet.add("Apple");
caseInsensitiveSet.add("banana");
// 迭代顺序按忽略大小写排序
关键并发操作与注意事项
它支持标准 Set 接口方法,但要注意以下行为特点:
- add() 和 remove() 是原子且线程安全的:多个线程并发调用不会破坏数据结构一致性。
- contains() 保证强一致性读取:返回的是某一时刻的确切快照结果(非最终一致)。
- 迭代器弱一致性:不抛 ConcurrentModificationException;可能反映部分更新,但不保证看到所有已添加/未删除元素 —— 适合“近似实时”场景,不适合强一致性校验逻辑。
- 不支持 null 元素:add(null) 会直接抛 NullPointerException。
- 没有批量操作(如addAll)的原子性保证:addAll 是逐个调用 add,整体不原子,若中途失败,已添加部分不会回滚。
典型高并发适用场景示例
例如:分布式任务调度系统中维护待执行任务的优先队列(按时间戳排序),多个线程持续提交/取消任务:
Setnew ConcurrentSkipListSet(Comparator.comparingLong(t -> t.scheduledAt));
// 线程A提交任务
pendingTasks.add(new ScheduledTask("send-email", System.currentTimeMillis() + 60_000));
// 线程B取消最早超时任务(利用 first()/pollFirst())
ScheduledTask earliest = pendingTasks.pollFirst(); // 原子获取并移除最小元素
注意:ConcurrentSkipListSet 没有 pollFirst/pollLast 方法(那是 Queue 接口的),但可通过 iterator().next() 获取首项,或转为 ConcurrentSkipListMap 使用其 keySet().pollFirst() —— 更推荐直接用 ConcurrentSkipListMap 如果需要头尾高效操作。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











