目录
概述
JDK提供的这些容器大部分在 java.util.concurrent 包中。我们这里挑选出了一些比较有代表性的并发容器类,来感受一下JDK自带的并发集合带来的便利:
在前面几篇也分别对这些类进行了使用场景介绍及源码解析:
ConcurrentHashMap
CopyOnWriteArrayList
ConcurrentLinkedQueue
BlockingQueue
ConcurrentSkipListMap
它实现了 ConcurrentMap
接口,提供了线程安全的有序映射
设计目的
ConcurrentSkipListMap
的设计目的是在多线程环境下提供一个有序的、线程安全的键值对映射结构。它通过跳表(Skip List)数据结构来实现高效的查找、插入和删除操作,同时保证在多线程并发访问时的数据一致性和线程安全性。与基于红黑树实现的 TreeMap
不同,跳表结构在并发环境下具有更好的扩展性和性能。
功能特性
有序性:ConcurrentSkipListMap
按照键的自然顺序(如果键实现了 Comparable
接口)或在构造时提供的 Comparator
进行排序。这使得可以方便地对映射中的键值对进行范围查询、遍历等操作。例如:
ConcurrentSkipListMap<Integer, String> map = new ConcurrentSkipListMap<>();
map.put(3, "three");
map.put(1, "one");
map.put(2, "two");
System.out.println(map.firstKey()); // 输出 1
System.out.println(map.lastKey()); // 输出 3
线程安全性:ConcurrentSkipListMap
是线程安全的,可以在多线程环境下安全地进行读写操作。多个线程可以同时读取映射,而写入操作(如插入、删除、更新)也能在不影响其他线程读取的情况下进行,通过内部的锁分段技术和无锁数据结构实现高效并发控制。
ExecutorService executorService = Executors.newFixedThreadPool(10);
ConcurrentSkipListMap<Integer, String> concurrentMap = new ConcurrentSkipListMap<>();
for (int i = 0; i < 10; i++) {
executorService.submit(() -> {
concurrentMap.put((int) (Math.random() * 100), "value");
});
}
executorService.shutdown();
高效的操作性能:跳表结构使得 ConcurrentSkipListMap
在查找、插入和删除操作上具有平均 O(logn) 的时间复杂度,其中 n 是映射中的元素数量。这使得它在处理大量数据时,性能表现良好。尤其在高并发读多写少的场景下,性能优势更为明显。
范围查询:支持范围查询操作,例如可以获取键在某个范围内的子映射。例如:
ConcurrentSkipListMap<Integer, String> map = new ConcurrentSkipListMap<>();
// 填充数据
map.put(1, "a");
map.put(3, "c");
map.put(5, "e");
ConcurrentNavigableMap<Integer, String> subMap = map.subMap(2, true, 4, true);
System.out.println(subMap); // 输出 {3=c}
与其他相关类对比
- 与
TreeMap
对比:- 线程安全性:
TreeMap
不是线程安全的,在多线程环境下需要外部同步机制来保证线程安全;而ConcurrentSkipListMap
本身就是线程安全的,适合多线程并发访问。 - 性能:在高并发场景下,
ConcurrentSkipListMap
由于采用跳表结构和更细粒度的并发控制,通常比使用外部同步的TreeMap
性能更好。
- 线程安全性:
- 与
ConcurrentHashMap
对比:- 有序性:
ConcurrentHashMap
不保证键的顺序,而ConcurrentSkipListMap
保证键的有序性。如果应用需要按照键的顺序进行操作,如范围查询、遍历等,ConcurrentSkipListMap
是更好的选择。 - 性能:
ConcurrentHashMap
在高并发读写场景下具有非常高的性能,尤其是在写操作较多的情况下。而ConcurrentSkipListMap
在范围查询和有序遍历方面表现更好,读操作性能也不错,但写操作相对ConcurrentHashMap
可能稍慢,因为需要维护跳表结构的有序性。
- 有序性:
适用场景
- 缓存系统:当缓存需要按照某种顺序(如访问时间、过期时间等)进行管理时,
ConcurrentSkipListMap
可以作为缓存的底层数据结构。例如,实现一个带有过期策略的缓存,按照过期时间排序,便于清理过期数据。 - 实时统计:在实时统计系统中,需要对数据按照某个维度(如时间戳、数值大小等)进行排序并实时更新。例如,统计一段时间内用户的登录次数,并按照登录次数排序展示,
ConcurrentSkipListMap
可以满足这种需求。 - 索引结构:在一些需要对数据建立索引,并支持多线程并发访问和范围查询的场景下,
ConcurrentSkipListMap
可以作为索引的实现方式。例如,在数据库的某些索引模块中,使用ConcurrentSkipListMap
可以提供高效的并发访问和范围查找功能。