BlockingQueue简介
BlockingQueue 继承了 Queue 接口,是队列的一种。Queue 和 BlockingQueue 都是在 Java 5
中加入的。阻塞队列(BlockingQueue)是一个在队列基础上又支持了两个附加操作的队列,两个附加操作(实现阻塞功能):
支持阻塞的插入方法put: 队列满时,队列会阻塞插入元素的线程,直到队列不满。
支持阻塞的移除方法take: 队列空时,获取元素的线程会等待队列变为非空
BlockingQueue常用方法示例
当队列满了无法添加元素,或者是队列空了无法移除元素时:
1. 抛出异常:add、remove、element
2. 返回结果但不抛出异常:offer、poll、peek
3. 阻塞:put、take
BlockingQueue应用场景
BlockingQueue 是线程安全的,我们在很多场景下都可以利用线程安全的队列来优雅地解决
我们业务自身的线程安全问题。
比如说,使用生产者/消费者模式的时候,我们生产者只需要往队列里添加元素,而消费者只需要从队列里取出它们就可以了。常见于类似于MQ之类的消息组件中。
因为阻塞队列是线程安全的,所以生产者和消费者都可以是多线程的,不会发生线程安全问
题。生产者/消费者直接使用线程安全的队列就可以,而不需要自己去考虑更多的线程安全问题。
这也就意味着,考虑锁等线程安全问题的重任从“你”转移到了“队列”上,降低了我们开发的
难度和工作量。
常见阻塞队列
BlockingQueue 接口的实现类都被放在了 juc 包中,它们的区别主要体现在存储结构上或对元素
操作上的不同,但是对于take与put操作的原理,却是类似的。
- ArrayBlockingQueue 基于数组结构实现的一个有界阻塞队列
- LinkedBlockingQueue 基于链表结构实现的一个无界阻塞队列,也可指定容量
- PriorityBlockingQueue 支持按优先级排序的无界阻塞队列(初始有界,可不断扩容)
- DelayQueue 基于PriorityBlockingQueue实现的无界阻塞队列
- SynchronousQueue 不存储元素的阻塞队列
- LinkedTransferQueue 基于链表结构实现的一个无界阻塞队列
- LinkedBlockingDeque 基于链表结构实现的一个双端阻塞队列
ArrayBlockingQueue
ArrayBlockingQueue是最典型的有界阻塞队列,其内部是用数组存储元素的,初始化时需要
指定容量大小,利用 ReentrantLock 实现线程安全。
在生产者-消费者模型中使用时,如果生产速度和消费速度基本匹配的情况下,使用
ArrayBlockingQueue是个不错选择;当如果生产速度远远大于消费速度,则会导致队列填满,大
量生产线程被阻塞。
使用独占锁ReentrantLock实现线程安全,入队和出队操作使用同一个锁对象,也就是只能有
一个线程可以进行入队或者出队操作;这也就意味着生产者和消费者无法并行操作,在高并发场
景下会成为性能瓶颈。
LinkedBlockingQueue
LinkedBlockingQueue是一个基于链表实现的阻塞队列,默认情况下,该阻塞队列的大小为
Integer.MAX_VALUE,由于这个数值特别大,所以 LinkedBlockingQueue 也被称作无界队列,
代表它几乎没有界限,队列可以随着元素的添加而动态增长,但是如果没有剩余内存,则队列
将抛出OOM错误。所以为了避免队列过大造成机器负载或者内存爆满的情况出现,我们在使用的
时候建议手动传一个队列的大小。
LinkedBlockingQueue内部由单链表实现,只能从head取元素,从tail添加元素。
LinkedBlockingQueue采用两把锁的锁分离技术实现入队出队互不阻塞,添加元素和获取元素都
有独立的锁,也就是说LinkedBl