本文部分摘自《Java 并发编程的艺术》
阻塞队列概述
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列,这两个附加的操作支持阻塞的插入和移除方法:
- 支持阻塞的插入方法:意思是当队列为满时,队列会阻塞插入元素的线程,直到队列不为满
- 支持阻塞的移除方法:意思是当队列为空时,获取元素的线程会等待队列变为非空
阻塞队列通常用在生产者和消费者的场景,生产者是向队列添加元素的线程,消费者是从队列获取元素的线程。阻塞队列就是生产者用来存放元素,消费者用来获取元素的容器
在阻塞队列不可用时,这两个附加操作提供了四种处理方式:
抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 | |
---|---|---|---|---|
插入方法 | add(o) | offer(o) | put(o) | offer(o, timeout, timeunit) |
移除方法 | remove(o) | poll() | take(o) | poll(o, timeout, timeunit) |
检查方法 | element() | peek() | — | — |
-
抛出异常
当队列满时,如果再往队列里插入元素,会抛出 IllegalStateException 异常。当队列空时,从队列里获取元素会抛出 NoSuchElementException 异常
-
返回特殊值
当往队列插入元素,会返回元素是否插入成功,成功返回 true,否则返回 false。如果是移除方法,则是从队列里取出一个元素,如果没有则返回 null
-
一直阻塞
当阻塞队列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断。当队列为空时,如果消费者线程从队列里 take 元素,队列会阻塞消费者线程,直到队列不为空
-
超时退出
当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程就会退出
Java 里的阻塞队列
JDK7 提供了 7 个阻塞队列,如下:
1. ArrayBlockingQueue
ArrayBlockingQueue 是一个用数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序,默认情况下不保证线程公平的访问队列
2. LinkedBlockingQueue
LinkedBlockingQueue 是一个用链表实现的有界阻塞队列,此队列的默认和最大长度为 Integer.MAX_VALUE,此队列按照先进先出的原则对元素进行排序
3. PriorityBlockingQueue
PriorityBlockingQueue 是一个支持优先级的无界阻塞队列,默认情况下元素采取自然顺序升序排序,也可以定义类实现 compareTo() 方法来指定元素排序规则,或者初始化队列时,指定构造参数 Comparator
4. DelayQueue
DelayQueue 是一个支持延时获取元素的无界阻塞队列,队列使用 PriorityQueue 来实现,队列中的元素必须实现 Delay 接口,在创建元素时可以指定多久才能从队列中获取当前元素,只有延迟期满才能从队列中提取元素
DelayQueue 可以运用在以下应用场景:
- 缓存系统的设计:可以用 DelayQueue 保证缓存元素的有效期,使用一个线程循环查询 DelayQueue,一旦能从中获取元素,表示缓存有效期到了
- 定时任务调度:使用 DelayQueue 保存当天将会执行的任务和执行时间,一旦从 DelayQueue 中获取到任务就开始执行
5. SynchronousQueue
SynchronousQueue 是一个不存储元素的阻塞队列,每一个 put 操作必须等待一个 take 操作,否则不能继续添加元素
6. LinkedTransferQueue
LinkedTransferQueue 是一个由链表结构组成的无界阻塞队列 TransferQueue 队列,相对于其他阻塞队列,LinkedTransferQueue 多了 tryTransfer 和 transfer 方法
-
transfer 方法
如果当前有消费者正在等待接收元素(消费者使用 take 方法或带时间限制的 poll 方法时),transfer 方法可以把生产者传入的元素立刻 transfer(传输)给消费者。如果没有消费者在等待接收元素,transfer 方法会将元素存放在队列的 tail 节点,并等到该元素被消费者消费了才返回
-
tryTransfer 方法
tryTransfer 方法是用来试探生产者传入的元素是否能直接传给消费者,如果没有消费者等待接收元素,则返回 false,和 transfer 方法的区别是 tryTransfer 方法无论消费者是否接收,方法立即返回,也可以带上时间限制
7. LinkedBlockingDeque
LinkedBlockingDeque 是一个由链表结构组成的双向阻塞队列,因为多了一个操作队列的入口,在多线程同时入队时,也就少了一半的竞争