java多线程安全集合

8 阅读4分钟

Java 为高并发场景专门设计的高性能线程安全集合。它们各自采用了不同的并发控制策略,适用于不同的场景。下面逐一介绍它们的实现原理、特点和适用场景。

介绍:这些线程安全的并发集合分别对应以下线程不安全的集合类:

线程安全类对应线程不安全类说明
ConcurrentHashMapHashMap无序键值对,高并发场景替代 HashMap 或 Hashtable
ConcurrentSkipListMapTreeMap有序键值对(基于跳表),高并发场景替代 TreeMap
ConcurrentSkipListSetTreeSet有序元素集(底层基于 ConcurrentSkipListMap),替代 TreeSet
CopyOnWriteArrayListArrayList读多写少场景替代 ArrayList,避免 ConcurrentModificationException
CopyOnWriteArraySetHashSet读多写少场景替代 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 接口)。