ConcurrentSkipListMap是Java并发包中唯一同时满足线程安全和天然有序的Map实现,基于跳表与CAS无锁算法,支持高并发下的插入、删除、查找及范围查询,适用于需实时排序与范围操作的场景。

ConcurrentSkipListMap 是 Java 并发包中唯一同时满足「线程安全」和「天然有序」两个硬性要求的 Map 实现。它不靠锁,也不依赖同步块,而是用跳表(Skip List)+ CAS 无锁算法,在高并发下稳定支持插入、删除、查找、范围遍历等操作。
适合用 ConcurrentSkipListMap 的典型场景
它不是 ConcurrentHashMap 的“有序版”,而是解决一类明确问题的专用工具:
- 需要按键排序后实时获取最小/最大 key(如
firstKey()、lastKey()) - 频繁做范围查询,比如按时间戳取最近一小时的任务(
subMap(from, to))、取所有小于某优先级的任务(headMap(upper)) - 多线程持续写入,同时有线程在遍历或轮询有序数据(如监控系统按等级聚合告警)
- 替代
Collections.synchronizedSortedMap(new TreeMap()),避免遍历时全局阻塞其他写操作
初始化和 key 类型必须注意的细节
它对 key 的约束比大多数 Map 更严格,错一步就直接抛异常:
- key 不能为
null——构造时或首次put就触发NullPointerException - key 必须可比较:要么实现
Comparable,要么构造时显式传入Comparator;否则运行时调用ceilingKey等方法会报ClassCastException - 自定义类作 key 时,
compareTo应与equals逻辑一致(虽然跳表本身不依赖equals,但业务代码常混用,易埋坑) - 推荐始终显式传
Comparator,避免隐式依赖 key 自身的compareTo,提升可读性和可控性
range 视图(subMap / headMap / tailMap)的真实行为
这些方法返回的不是快照,而是原 map 的**实时、可变、弱一致性视图**:
- 对子 map 的
put或remove会直接影响原 map,反之亦然 - 迭代子 map 时,可能看到“半完成”状态(例如某次插入只连上部分层级),这是无锁设计的合理代价
-
subMap(k1, k2)是左闭右开区间(k1 ≤ key < k2),和TreeMap一致,但容易误读为闭区间 - 避免在高频路径中调用
subMap.size()——它需遍历计数,不是 O(1)
性能与内存的实际取舍
它快得有代价,稳得有开销:
- 平均时间复杂度是 O(log n),和
TreeMap相当,但所有操作都无锁,吞吐更平稳 - 内存比
TreeMap高 20%–40%,因为每个节点要存 log n 层指针(索引层) - 单次插入/删除比
ConcurrentHashMap慢,因涉及多层 CAS 和指针更新;但整体吞吐在读写混合且需有序时更优 - 不适用于纯高性能缓存场景(此时
ConcurrentHashMap更合适),也不适用于仅需排序但并发不高的场景(此时TreeMap更轻量)

















