# 前言
阻塞队列是当队列空或满的情况下,能够阻塞消费者或生产者,然后在队列有数据或有空位置的时候,能够唤醒被阻塞的线程。
# 常见阻塞队列
Java 中有 7 种常见的阻塞队列:
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- DelayQueue
- SynchronousQueue
- LinkedTransferQueue
- LinkedBlockingDeque
他们的类图结构如图:
可以看出,他们都继承了一个队列的抽象类 AbstractQueue,实现了阻塞队列接口 BlockingQueue,AbstractQueue 和 BlockingQueue 都实现或继承了 Queue 接口。
# 主要区别
ArrayBlockingQueue 是基于数组实现的有界阻塞队列,先进先出,可指定是否使用公平锁,使用循环双指针来指定头结点和尾结点位置。多线程使用一把锁来同步所有操作,使用两个 condition 来等待队列非空或非满。
LinkedBlockingQueue 是基于链表实现的有界阻塞队列,两个节点分别指向头结点和尾结点。通过双锁算法用两个锁来分别同步移除和插入操作,两个 condition 来等待队列非空或非满,直接使用默认的非公平锁。
PriorityBlockingQueue 是具有优先级的无界阻塞队列,底层使用数组实现,队列默认大小是 11,最大值是整型 - 8,可以自定义优先级比较器。使用一个锁来控制所有操作的同步,一个 condition 来等待队列非空,扩容操作通过 CAS 锁进行同步。
DelayQueue 是具有延时的无界阻塞队列,底层使用优先队列实现,元素必须继承 Delayed 接口,实现 getDelay 和 compareTo 方法。使用一个锁来同步所有操作,用 Leader/Follower 模式来移除元素,一个 condition 来等待队列非空或头结点移除。
SynchronousQueue 是没有容量的阻塞队列,通常用来在生产者和消费者之间传输,元素状态分为 消费、生产、匹配状态。每一个 put 操作必须等待一个 take 操作,否则不能继续 put,反之亦然。
LinkedTransferQueue 是由链表组成的无界阻塞队列,实现了 TransferQueue 接口,相较于其他阻塞队列多了 tryTransfer 和 transfer 方法,在生产者插入数据时,如果有消费者来消费数据,直接传递给消费者。
LinkedBlockingDeque 是基于链表的有界双向阻塞队列,可以运用在窃取算法中。
# 主要方法
主要方法有插入、移除和检查,阻塞策略有抛异常、返回特殊值(null 或 false,视情况而定)、阻塞和超时(先阻塞,超时后返回特殊值)。
方法类型 | 抛出异常 | 特殊值 | 阻塞 | 超时 |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e, timeout, unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
检查 | element() | peek() | -- | -- |
抛出异常的方式实现继承自 AbstractQueue,特殊值、阻塞和超时的方法,都实现了 BlockingQueue。