ConcurrentSkipListMap是高并发下支持排序的线程安全Map优选,基于跳跃表实现非阻塞的插入、删除和查找操作,提供O(log n)平均时间复杂度,相比synchronized TreeMap提升吞吐量。它实现NavigableMap接口,支持有序访问如firstEntry、subMap等范围查询,适用于任务调度、优先级队列等场景;使用时需注意键必须可比较,不支持null键值,迭代器为弱一致性,推荐结合putIfAbsent等原子操作避免竞态条件,适合读写混合且需自然排序的高并发环境。
在高并发场景下,如果需要一个支持排序的线程安全Map,ConcurrentSkipListMap 是比 TreeMap 或 Collections.synchronizedSortedMap 更优的选择。它基于跳跃表(SkipList)实现,提供非阻塞的并发插入、删除和查找操作,同时保持键的自然顺序或自定义排序。
ConcurrentSkipListMap 实现了 NavigableMap 接口,支持有序访问,如获取小于/大于某个键的子集、获取第一个或最后一个条目等。
创建并使用示例:
ConcurrentSkipListMapmap = new ConcurrentSkipListMap<>(); map.put(3, "Three"); map.put(1, "One"); map.put(4, "Four"); // 输出按 key 升序排列 System.out.println(map); // {1=One, 3=Three, 4=Four}
支持自定义比较器:
ConcurrentSkipListMapreverseMap = new ConcurrentSkipListMap<>(Collections.reverseOrder()); reverseMap.put("apple", 1); reverseMap.put("banana", 2); System.out.println(reverseMap); // {banana=2, apple=1}
与 TreeMap 相比,ConcurrentSkipListMap 在多线程环境下无需外部同步即可安全操作,其内部采用无锁算法(CAS)实现高效并发控制。
对比 synchronized TreeMap:后者在高并发下容易成为瓶颈,因为每次操作都需获取对象锁;而 ConcurrentSkipListMap 只锁定局部节点,提升吞吐量。
利用其 NavigableMap 特性,可以高效执行范围查询和定位操作:
这些方法返回的视图也是线程安全的,适合用于实时监控、滑动窗口、优先级队列等场景。
例如:实现一个按时间戳排序的任务调度缓存:
ConcurrentSkipListMaptasks = new ConcurrentSkipListMap<>(); // 添加任务(时间戳作为 key) tasks.put(System.currentTimeMillis() + 5000, () -> System.out.println("Delay task")); // 扫描并执行到期任务 long now = System.currentTimeMillis(); ConcurrentNavigableMap expired = tasks.headMap(now, true); expired.values().forEach(Runnable::run); expired.clear(); // 安全清除已执行任务
虽然功能强大,但使用时仍需注意以下几点:
建议配合原子操作或业务逻辑判断,避免竞态条件。例如用 putIfAbsent 实现幂等缓存:
String result = map.putIfAbsent(key, computeExpensiveValue()); if (result == null) { result = map.get(key); // 刚放入的值 }
基本上就这些。ConcurrentSkipListMap 在需要排序+并发的场景中非常实用,合理使用能显著提升系统性能和可维护性。不复杂但容易忽略细节,掌握好它的特性就能发挥出最大价值。