11 · 并发容器全家桶

发布时间:2026/11/4 12:14:42
11 · 并发容器全家桶

一句话定位:除了 ConcurrentHashMap,JUC 还提供了一整套线程安全容器,各有适用场景。


1. 为什么不用 Collections.synchronizedXxx

List<String>list=Collections.synchronizedList(newArrayList<>());
  • 本质是给每个方法加synchronized,整个容器一把锁,并发度=1。
  • 复合操作仍不安全(遍历时要手动加锁)。
  • 性能差,JUC 容器是更好的选择。

2. CopyOnWriteArrayList:写时复制

核心思想:读不加锁,写的时候复制一份新数组改,改完替换引用。

CopyOnWriteArrayList<String>list=newCopyOnWriteArrayList<>();// add 源码精髓publicbooleanadd(Ee){synchronized(lock){// 写加锁Object[]old=getArray();Object[]newArr=Arrays.copyOf(old,old.length+1);// 复制新数组newArr[old.length]=e;setArray(newArr);// volatile 替换引用returntrue;}}publicEget(intindex){return(E)getArray()[index];// 读完全不加锁!}
优点缺点
读无锁,读性能极高写要复制整个数组,内存+性能开销大
遍历时不抛 ConcurrentModificationException数据弱一致(读可能读到旧快照)

适用:读多写极少、数据量不大。典型:监听器列表、配置白名单、路由表。
不适用:频繁写、大数据量(每次写都复制,OOM 风险)。


3. CopyOnWriteArraySet

基于 CopyOnWriteArrayList 实现的 Set,特性相同,适合读多写少的集合去重场景。


4. ConcurrentLinkedQueue:无锁队列

基于CAS的无界非阻塞队列(不是阻塞队列):

ConcurrentLinkedQueue<Task>queue=newConcurrentLinkedQueue<>();queue.offer(task);// 入队(CAS,不阻塞)Taskt=queue.poll();// 出队,空返回 null(不阻塞)
  • 高并发下性能好(无锁 CAS)。
  • 空时poll返回 null,不阻塞(和 BlockingQueue 的 take 不同)。
  • size()要遍历,是 O(n),别频繁调用。

适用:高并发、不需要阻塞等待的队列场景。需要阻塞用 BlockingQueue(见 12 篇)。


5. ConcurrentSkipListMap / Set:并发有序容器

跳表(SkipList)实现的并发有序Map/Set,相当于并发版的 TreeMap。

ConcurrentSkipListMap<Integer,String>map=newConcurrentSkipListMap<>();map.put(3,"c");map.put(1,"a");map.put(2,"b");// 自动按 key 排序,支持范围查询map.firstKey();// 1map.headMap(2);// 小于 2 的部分map.ceilingKey(2);// >= 2 的最小 key
跳表结构(多层索引链表,查询 O(log n)): L2: 1 ────────────→ 9 L1: 1 ──→ 4 ──────→ 9 L0: 1 → 3 → 4 → 6 → 9 (底层完整链表)
TreeMapConcurrentSkipListMap
线程安全❌✅
实现红黑树跳表
有序✅✅

适用:需要并发 + 有序 + 范围查询。比如排行榜、按时间排序的事件表。


6. 容器选型速查

需求推荐容器
并发 MapConcurrentHashMap
并发有序 MapConcurrentSkipListMap
读多写极少的 ListCopyOnWriteArrayList
高并发非阻塞队列ConcurrentLinkedQueue
需要阻塞的队列(生产消费)BlockingQueue(见 12 篇)
并发 SetConcurrentHashMap.newKeySet()
读多写少的 SetCopyOnWriteArraySet

7. 踩坑与最佳实践

  • ⚠️ CopyOnWrite 系列写代价大,只用于读多写极少、数据量小的场景,否则 OOM。
  • ⚠️ CopyOnWrite 读的是快照,弱一致(刚写的可能读不到),别用于强一致场景。
  • ⚠️ConcurrentLinkedQueue.size()是 O(n),判空用isEmpty()不要用size()==0。
  • ✅ 需要并发 Set 用ConcurrentHashMap.newKeySet(),比 CopyOnWriteArraySet 更通用。
  • ✅ 别用Collections.synchronizedXxx,JUC 容器性能更好。
  • 💡 遍历 CopyOnWriteArrayList 时其他线程的修改不影响当前遍历(基于快照),不会抛并发修改异常。

8. 本篇小结

  • CopyOnWriteArrayList:写时复制,读无锁,适合读多写极少。
  • ConcurrentLinkedQueue:CAS 无锁队列,不阻塞。
  • ConcurrentSkipListMap:跳表实现的并发有序 Map。
  • 按“是否有序、是否阻塞、读写比例”选择合适容器。