JUC包下常用的并发容器速览
前言
普通集合(ArrayList、HashMap)全部线程不安全。JUC包下并发容器,专门解决多线程并发读写安全问题。
JUC容器分为四大类:
-
并发Map:ConcurrentHashMap、ConcurrentSkipListMap
-
并发List/Set:CopyOnWriteArrayList、CopyOnWriteArraySet
-
阻塞队列(BlockingQueue):七大常用阻塞队列(线程池核心)
-
无锁并发队列:ConcurrentLinkedQueue
一、ConcurrentHashMap 🔴
1.1 整体定位
线程安全的键值对集合,替代 Hashtable。Hashtable 是全局锁、并发极差;ConcurrentHashMap 锁粒度极小,并发性能极高。
1.2 JDK1.7 底层原理
数据结构:数组 + 链表 + 分段锁 Segment
-
将整个Map分为16个Segment分段
-
每个Segment独立持有一把ReentrantLock
-
不同Segment的读写互不影响,大幅提升并发
缺点:锁粒度依旧偏大、最高并发度16、无法扩容Segment、性能瓶颈明显。
1.3 JDK1.8 底层原理 🔴
废弃分段锁,数据结构升级为:数组 + 链表 + 红黑树
加锁机制:CAS + synchronized 锁首节点
不再锁分段,直接锁当前哈希桶的链表头/树根节点,锁粒度极致细化。
扩容与树化阈值
-
扩容阈值:元素数量达到容量 0.75 倍触发扩容,容量翻倍
-
链表转红黑树:链表长度 > 8 且 数组长度 ≥ 64
-
红黑树退链表:树节点数量 ≤ 6
-
链表长度8但数组小于64:只扩容,不树化
并发扩容机制
支持多线程协助扩容。一个线程扩容,其他写入线程会帮忙迁移数据,极大提升扩容效率。
为什么不用ReentrantLock?
synchronized 在JDK1.6后经过锁升级优化,性能不输ReentrantLock;且原生关键字更轻量化、内存开销更小、JVM可深度优化。
1.4 关键坑点 🔴
-
不保证全局一致性:get/size 是弱一致性,并发下无法精准统计总数
-
key/value 不能为null(HashMap可以),防止二义性
-
只能保证单个put原子性,复合操作(先查后改)依旧线程不安全
二、ConcurrentSkipListMap
2.1 定位
并发有序Map,底层是跳表。对应非并发的 TreeMap。
2.2 特点
-
key 自动排序
-
基于CAS无锁实现,并发性能高于ConcurrentHashMap
-
适合有序、高并发场景
缺点:内存占用高,跳表每层都需要索引节点。
场景:排行榜、有序并发KV存储、时间线数据。
三、CopyOnWriteArrayList
3.1 核心原理
写时复制。所有修改操作(add/set/remove)不会直接改原数组:
-
加锁(ReentrantLock)
-
复制原数组,生成一个长度+1/-1的新数组
-
修改新数组
-
引用指针指向新数组
读操作完全无锁,直接读原数组。
3.2 优缺点
优点:读写分离、读性能爆炸高、并发极高
缺点:
-
写操作需要复制数组,内存开销大、写性能极差
-
弱一致性:读的是旧数组快照,无法读到实时最新数据
-
数组量大时,每次复制极度耗内存,容易GC频繁
3.3 使用场景
读多写少:白名单、配置列表、字典数据、本地缓存
绝对禁止:频繁增删改业务
四、CopyOnWriteArraySet
底层直接基于 CopyOnWriteArrayList 实现。
去重原理:新增元素时遍历数组对比,存在则不新增。
特性完全和CopyOnWriteArrayList一致:读无锁、写复制、弱一致性、适合读多写少。
五、ConcurrentLinkedQueue(无锁并发队列)
5.1 定位
JUC唯一无锁、高并发、非阻塞队列。底层基于 CAS + 链表。
5.2 特点
-
全程无锁,自旋CAS保证线程安全
-
性能高于所有阻塞队列
-
线程不会阻塞,失败自旋重试
缺点:大量并发会导致CAS自旋空转,消耗CPU
场景:高并发消息生产、秒杀异步队列、高性能消息缓冲
六、BlockingQueue 阻塞队列(线程池核心)
所有阻塞队列统一特性:入队、出队可阻塞、支持超时、线程同步通信,基于AQS实现。
6.1 ArrayBlockingQueue
-
底层:定长数组
-
有界队列,必须初始化容量
-
全局一把ReentrantLock,读写互斥
-
阻塞、公平/非公平可配置
场景:任务量可控、防止无限堆积OOM,线程池常用队列
6.2 LinkedBlockingQueue
-
底层:单向链表
-
默认无界,可指定有界
-
两把锁:读锁、写锁分离,读写互不阻塞,并发高于Array
坑点:不指定容量就是无界队列,任务无限堆积直接OOM
6.3 SynchronousQueue
零容量队列,不存储任何元素
生产一个任务,必须等待消费者消费完成,生产者才返回
场景:newCachedThreadPool 线程池专用,任务不排队、直接新建线程
6.4 PriorityBlockingQueue
优先级阻塞队列,底层最小堆,无界,元素自带排序
场景:优先级任务调度、超时任务优先执行
6.5 DelayQueue
延时阻塞队列,元素必须实现延时接口
元素到期才能被取出,底层基于PriorityBlockingQueue
场景:订单超时关闭、延时重试、定时任务
6.6 LinkedTransferQueue
性能最强阻塞队列,融合SynchronousQueue+LinkedQueue特性,支持转移机制
七、JUC容器核心对比 🔴
7.1 锁机制区别
-
ConcurrentHashMap:CAS + synchronized 细粒度锁
-
CopyOnWrite系列:ReentrantLock 写锁,读无锁
-
BlockingQueue:AQS + ReentrantLock 可阻塞
-
ConcurrentLinkedQueue:纯CAS无锁、不阻塞
7.2 强一致性 / 弱一致性
-
阻塞队列、ConcurrentHashMap写操作:强一致
-
CopyOnWrite、ConcurrentHashMap读操作:弱一致(可能读到旧数据)
7.3 场景选型 🔴
-
键值存储、高并发读写 → ConcurrentHashMap
-
键值存储、需要排序 → ConcurrentSkipListMap
-
列表、读多写少 → CopyOnWriteArrayList
-
高并发消息队列、不允许阻塞 → ConcurrentLinkedQueue
-
线程池任务排队、需要阻塞控量 → Array/LinkedBlockingQueue
-
延时任务 → DelayQueue
-
一对一任务传递 → SynchronousQueue