Java 为高并发场景专门设计的高性能线程安全集合。它们各自采用了不同的并发控制策略,适用于不同的场景。下面逐一介绍它们的实现原理、特点和适用场景。
介绍:这些线程安全的并发集合分别对应以下线程不安全的集合类:
| 线程安全类 | 对应线程不安全类 | 说明 |
|---|---|---|
ConcurrentHashMap |
HashMap |
无序键值对,高并发场景替代 HashMap 或 Hashtable |
ConcurrentSkipListMap |
TreeMap |
有序键值对(基于跳表),高并发场景替代 TreeMap |
ConcurrentSkipListSet |
TreeSet |
有序元素集(底层基于 ConcurrentSkipListMap),替代 TreeSet |
CopyOnWriteArrayList |
ArrayList |
读多写少场景替代 ArrayList,避免 ConcurrentModificationException |
CopyOnWriteArraySet |
HashSet |
读多写少场景替代 HashSet(底层使用 CopyOnWriteArrayList 实现) |
1. ConcurrentHashMap
底层实现
- JDK 1.7 :采用 分段锁(Segment) 技术,将整个 Map 分成多个 Segment,每个 Segment 独立加锁,允许多个线程同时操作不同 Segment。
- JDK 1.8+ :取消分段锁,改用 CAS + synchronized 对节点进行更细粒度的控制。底层数据结构与
HashMap类似(数组 + 链表 + 红黑树),写操作只锁定当前操作的桶(或树节点),读操作几乎不加锁(使用volatile保证可见性)。
特点
- 高并发读写,吞吐量远高于
Hashtable或Collections.synchronizedMap。 - 不允许
null键或值。 - 提供原子性复合操作:
putIfAbsent()、remove()、replace()等。 - 迭代器是 弱一致性 (不会抛出
ConcurrentModificationException,但遍历时可能不反映最新修改)。
适用场景
- 高并发环境下的大规模 Map 操作,是 首选的并发 Map 实现。
2. ConcurrentSkipListMap
底层实现
基于 跳表(Skip List) 数据结构,是一种以空间换时间的并发有序 Map。跳表通过多层索引实现近似二分查找,插入、删除、查找的平均时间复杂度为 O(log n) 。
特点
- 线程安全,支持高并发读写。
- 保持键的有序性 ,实现
ConcurrentNavigableMap接口,提供了subMap()、headMap()、tailMap()等视图方法。 - 并发控制采用 CAS 和锁(在跳表节点层面精细控制),性能优秀。
- 不允许
null键或值。
适用场景
- 需要 有序且高并发 的 Map,例如实现并发缓存、排行榜、范围查询等。
- 是
TreeMap的并发替代品。
3. ConcurrentSkipListSet
底层实现
本质上是一个 ConcurrentSkipListMap 的包装 ,所有元素作为 Map 的键,值统一为 Boolean.TRUE。
特点
- 线程安全、有序(按自然顺序或自定义
Comparator排序)。 - 支持高并发读写。
- 迭代器是弱一致性。
适用场景
- 需要 有序且高并发 的 Set,例如需要快速判断元素是否存在、保持元素有序,且允许多线程并发访问。
4. CopyOnWriteArrayList
底层实现
采用 写时复制(Copy-On-Write) 策略:
- 内部维护一个
volatile数组作为存储。 - 读操作 (
get()、iterator())直接访问当前数组,不加锁,性能极高。 - 写操作 (
add()、set()、remove())先复制一份新数组,在新数组上修改,然后将数组引用指向新数组。复制过程中,读操作仍可并发访问旧数组,保证读线程不被阻塞。
特点
- 读操作无锁,写操作因复制数组而开销较大(适合读多写少的场景)。
- 迭代器是 快照 风格,创建后不会受后续修改影响,永远不会抛出
ConcurrentModificationException。 - 内存占用较高(写操作会临时产生两份数组)。
适用场景
- 读远多于写 的集合,例如配置信息、监听器列表、缓存等。
5. CopyOnWriteArraySet
底层实现
直接 包装 CopyOnWriteArrayList ,所有操作委托给内部的 CopyOnWriteArrayList。
特点
- 继承
AbstractSet,元素唯一性通过内部 List 的addIfAbsent()保证。 - 具备与
CopyOnWriteArrayList相同的读写特性:读无锁、写复制。 - 由于底层是 List,
contains()操作是 O(n) 复杂度(相对于HashSet的 O(1))。
适用场景
- 与
CopyOnWriteArrayList类似,适用于 读多写少、且元素唯一 的场景。
对比总结
| 集合类 | 数据结构 | 是否有序 | 锁机制 | 读写特性 | 适用场景 |
|---|---|---|---|---|---|
ConcurrentHashMap |
数组+链表+红黑树 | 无序 | JDK 1.8+:CAS + synchronized 锁桶/节点 | 读几乎无锁,写锁粒度细 | 高并发通用 Map |
ConcurrentSkipListMap |
跳表 | 有序 | CAS + 节点锁 | 读写均高效 | 需要有序、高并发 Map |
ConcurrentSkipListSet |
跳表(包装 Map) | 有序 | 同 ConcurrentSkipListMap |
读写均高效 | 需要有序、高并发 Set |
CopyOnWriteArrayList |
数组(写时复制) | 保持插入顺序 | 写时复制(写锁),读无锁 | 读极快,写慢 | 读远多于写的 List |
CopyOnWriteArraySet |
数组(包装 List) | 保持插入顺序 | 同 CopyOnWriteArrayList |
读极快,写慢 | 读远多于写的 Set |
选型建议
- Map :默认首选
ConcurrentHashMap;若需要键有序,使用ConcurrentSkipListMap。 - Set :若需高并发且无需排序,可考虑
ConcurrentHashMap包装的newKeySet()(JDK 8+,ConcurrentHashMap.newKeySet());若需有序,用ConcurrentSkipListSet;若读远多于写且元素唯一性要求不高,可用CopyOnWriteArraySet。 - List :若读远多于写,用
CopyOnWriteArrayList;否则可考虑用Collections.synchronizedList或ConcurrentLinkedQueue等替代(但需注意ConcurrentLinkedQueue不是 List 接口)。