Java并发编程(十六)-JUC Collections
参考文献
JUC Collections
集合框架
Queue
Blocking Queue
阻塞队列
-
java.util.concurrent.ArrayBlockingQueue
:最基础且开发中最常用的阻塞队列,底层采用数组实现的有界队列,初始化需要指定队列的容量.ArrayBlockingQueue
是如何保证线程安全的呢?它内部是使用了一个重入锁ReentrantLock
,并搭配notEmpty、notFull
两个条件变量Condition
来控制并发访问.从队列读取数据时,如果队列为空,那么会阻塞等待,直到队列有数据了才会被唤醒.如果队列已经满了,也同样会进入阻塞状态,直到队列有空闲才会被唤醒.
-
java.util.concurrent.LinkedBlockingQueue
:内部采用的数据结构是链表,队列的长度可以是有界或者无界的.- 初始化不需要指定队列长度,默认是
Integer.MAX_VALUE.LinkedBlockingQueue
内部使用了takeLock、putLock
两个重入锁ReentrantLock
,以及notEmpty、notFull
两个条件变量Condition
来控制并发访问.采用读锁和写锁的好处是可以避免读写时相互竞争锁的现象,所以相比于ArrayBlockingQueue,LinkedBlockingQueue
的性能要更好.
- 初始化不需要指定队列长度,默认是
-
java.util.concurrent.PriorityBlockingQueue
:采用最小堆实现的优先级队列,队列中的元素按照优先级进行排列,每次出队都是返回优先级最高的元素.PriorityBlockingQueue
内部是使用了一个ReentrantLock
以及一个条件变量Condition
,notEmpty
来控制并发访问,不需要notFull
是因为PriorityBlockingQueue
是无界队列,所以每次put
都不会发生阻塞.PriorityBlockingQueue
底层的最小堆是采用数组实现的,当元素个数大于等于最大容量时会触发扩容,在扩容时会先释放锁,保证其他元素可以正常出队,然后使用CAS
操作确保只有一个线程可以执行扩容逻辑.
-
java.util.concurrent.DelayQueue
,一种支持延迟获取元素的阻塞队列,常用于缓存、定时任务调度等场景.DelayQueue
内部是采用优先级队列PriorityQueue
存储对象.DelayQueue
中的每个对象都必须实现Delayed
接口,并重写compareTo
和getDelay
方法.向队列中存放元素的时候必须指定延迟时间,只有延迟时间已满的元素才能从队列中取出.
-
java.util.concurrent.SynchronousQueue
,又称无缓冲队列.比较特别的是SynchronizedQueue
内部不会存储元素.- 与
ArrayBlockingQueue、LinkedBlockingQueue
不同,SynchronizedQueue
直接使用CAS
操作控制线程的安全访问.其中put
和take
操作都是阻塞的,每一个put
操作都必须阻塞等待一个take
操作,反之亦然.所以SynchronizedQueue
可以理解为生产者和消费者配对的场景,双方必须互相等待,直至配对成功.在 JDK 的线程池Executors.newCachedThreadPool
中就存在SynchronousQueue
的运用,对于新提交的任务,如果有空闲线程,将重复利用空闲线程处理任务,否则将新建线程进行处理.
- 与
-
java.util.concurrent.LinkedTransferQueue
,一种特殊的无界阻塞队列,可以看作LinkedBlockingQueues
、SynchronousQueue
(公平模式)、ConcurrentLinkedQueue
的合体.- 与
SynchronousQueue
不同的是,LinkedTransferQueue
内部可以存储实际的数据,当执行put
操作时,如果有等待线程,那么直接将数据交给对方,否则放入队列中.与LinkedBlockingQueues
相比,LinkedTransferQueue
使用CAS
无锁操作进一步提升了性能.
- 与
全类名 | 底层实现 | 有无界 | 使用场景 |
---|---|---|---|
java.util.concurrent.ArrayBlockingQueue |
数组 | 有界 | 适合有固定大小的任务队列,常用于生产者-消费者模型 |
java.util.concurrent.LinkedBlockingQueue |
链表 | 有界 | 适用于大多数生产者-消费者场景,提供可选的容量限制 |
java.util.concurrent.PriorityBlockingQueue |
数组 | 无界 | 用于优先级任务调度,元素按照优先级顺序出队 |
java.util.concurrent.DelayQueue |
链表 | 无界 | 用于任务延迟执行,只有延迟期满的元素才能从队列中取出 |
java.util.concurrent.SynchronousQueue |
/ | / | 不存储元素,适用于传递型设计,任务直接交接,常用于线程池中的任务提交 |
java.util.concurrent.LinkedTransferQueue |
链表 | 无界 | 用于高效的传输场景,可在没有消费者时等待元素被消费 |
java.util.concurrent.LinkedBlockingDeque |
链表 | 有界 | 适用于需要双端操作的生产者-消费者场景,允许在队列两端插入和移除元素 |
接口/行为 | 队列满抛出异常 | 返回特定值 | 一直阻塞 | 阻塞一段时间 |
---|---|---|---|---|
新增元素 | add(e) |
offer(e) |
put(e) |
offer(e,time,unit) |
删除元素 | remove() |
poll() |
take() |
poll(time,unit) |
查看元素 | element() |
peek() |
/ | / |
非阻塞队列
java.util.concurrent.ConcurrentLinkedQueue
,它是一个采用双向链表实现的无界并发非阻塞队列,它属于LinkedQueue
的安全版本.ConcurrentLinkedQueue
内部采用 CAS 操作保证线程安全,这是非阻塞队列实现的基础,相比ArrayBlockingQueue、LinkedBlockingQueue
具备较高的性能.java.util.concurrent.ConcurrentLinkedDeque
,也是一种采用双向链表结构的无界并发非阻塞队列.与 ConcurrentLinkedQueue 不同的是,ConcurrentLinkedDeque 属于双端队列,它同时支持 FIFO 和 FILO 两种模式,可以从队列的头部插入和删除数据,也可以从队列尾部插入和删除数据,适用于多生产者和多消费者的场景.
第三方无锁队列
Disruptor
JCTools
-
JCTools 是适用于 JVM 并发开发的工具,主要提供了一些 JDK 确实的并发数据结构,例如非阻塞 Map、非阻塞 Queue 等。其中非阻塞队列可以分为四种类型,可以根据不同的场景选择使用。
-
Spsc 单生产者单消费者;
-
Mpsc 多生产者单消费者;
-
Spmc 单生产者多消费者;
-
Mpmc 多生产者多消费者。
-
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HoleLin's Blog!