BlockingQueue
BlockingQueue 是阻塞队列接口,是生产者消费者模型的核心工具。它在队列为空或满时阻塞线程,从而实现线程间协作和流量调节。
# 1. 基本模型
Producer
│ put / offer
▼
BlockingQueue
│ take / poll
▼
Consumer
队列空时消费者等待,队列满时生产者等待。
# 2. 方法分类
| 操作 | 抛异常 | 返回特殊值 | 阻塞 | 超时 |
|---|---|---|---|---|
| 插入 | add | offer | put | offer(e, time, unit) |
| 移除 | remove | poll | take | poll(time, unit) |
| 查看 | element | peek | 无 | 无 |
生产代码常用:
put/take:明确要阻塞。offer/poll带超时:避免永久等待。
# 3. ArrayBlockingQueue
有界数组阻塞队列。
BlockingQueue<Task> queue = new ArrayBlockingQueue<>(1000);
特点:
- 容量固定。
- 内存可控。
- 适合做线程池任务队列或生产消费缓冲。
有界队列能形成反压,是生产环境更稳的默认选择。
# 4. LinkedBlockingQueue
链表阻塞队列。
BlockingQueue<Task> queue = new LinkedBlockingQueue<>(1000);
注意:如果不指定容量,默认容量非常大,可能导致任务无限堆积。
new LinkedBlockingQueue<>(); // 不推荐生产中无界使用
# 5. SynchronousQueue
SynchronousQueue 不存储元素,每次 put 必须等待 take。
Producer put
│ 等 Consumer take
▼
直接交接
适合直接交接任务,例如 newCachedThreadPool 使用它。但如果没有控制最大线程数,可能导致线程快速膨胀。
# 6. PriorityBlockingQueue
并发优先级队列。
BlockingQueue<Task> queue = new PriorityBlockingQueue<>(
100,
Comparator.comparingInt(Task::priority)
);
注意:
- 是无界队列,可能堆积。
- 出队按优先级。
- 相同优先级顺序不一定稳定。
# 7. DelayQueue
DelayQueue 中元素必须实现 Delayed,只有到期后才能被取出。
适合:
- 延迟任务。
- 超时关闭。
- 缓存过期。
模型:
任务 A 到期时间 10:00
任务 B 到期时间 10:05
take 只返回已到期任务
# 8. 生产者消费者示例
BlockingQueue<Order> queue = new ArrayBlockingQueue<>(1000);
executor.execute(() -> {
while (!Thread.currentThread().isInterrupted()) {
Order order = queue.take();
process(order);
}
});
queue.put(order);
要处理 InterruptedException:
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
# 9. 反压
有界阻塞队列可以限制生产速度。
消费者慢
│
▼
队列逐渐满
│
▼
生产者 put 阻塞或 offer 超时
│
▼
上游感知压力
无界队列没有这种保护,可能把压力变成内存问题。
# 10. 选型
| 场景 | 推荐 |
|---|---|
| 固定容量生产消费 | ArrayBlockingQueue |
| 链表队列且容量可控 | LinkedBlockingQueue(capacity) |
| 直接交接 | SynchronousQueue |
| 优先级任务 | PriorityBlockingQueue |
| 延迟任务 | DelayQueue |
# 专家实践与边界
BlockingQueue 不只是一个队列,更是生产者和消费者之间的背压边界。容量、超时、拒绝和关闭协议决定了系统在峰值流量下是稳定退让还是直接崩溃。
生产者
│ offer/put
▼
有界队列
│ take/poll
▼
消费者
| 方法 | 行为 | 适合场景 |
|---|---|---|
add | 满时抛异常 | 很少直接用 |
offer | 满时返回 false | 非阻塞尝试 |
put | 满时阻塞 | 可接受等待 |
offer(timeout) | 超时返回 | 生产系统更可控 |
take | 空时阻塞 | 消费者循环 |
poll(timeout) | 超时返回 | 可感知关闭信号 |
生产环境优先使用有界队列。无界队列会把压力转移到内存,短时间看似不丢任务,长期可能导致 OOM 和雪崩。
# Tips 快问快答
Q:BlockingQueue 解决什么问题? A:线程安全队列和阻塞协作,常用于生产者消费者。
Q:put 和 offer 有什么区别?
A:put 满时一直阻塞,offer 可立即返回或带超时。
Q:take 和 poll 有什么区别?
A:take 空时阻塞,poll 可立即返回 null 或带超时。
Q:为什么推荐有界队列? A:有界队列能形成反压,避免任务无限堆积。
Q:LinkedBlockingQueue 默认有界吗? A:默认容量非常大,生产中应显式指定容量。
Q:SynchronousQueue 存元素吗? A:不存,生产者和消费者直接交接。
Q:InterruptedException 怎么处理? A:通常恢复中断标记并退出或向上抛出。
Q:BlockingQueue 能替代 wait/notify 吗? A:生产消费场景下通常可以,而且更推荐。