跳到正文
Elaine Blog
返回

BlockingQueue 与生产者消费者

并发编程

生产者消费者模型的关键不是启动两个线程,而是定义队列容量和过载行为。无界队列会把短期流量峰值变成内存增长;有界 队列则让系统在过载时等待、拒绝或降级。

Table of contents

Open Table of contents

运行有界队列实验

下载背压样例下载, 运行:

java -cp target/classes dev.elaine.concurrency.BackpressureSample

输出为:

第一个事件=true
队列已满时第二个事件=false
消费=event-1

第二次 offer 在 50 毫秒内得不到容量后返回 false。调用方能据此返回忙碌、记录丢弃、写入备用存储或让上游降速。

四组 API 表达不同失败策略

行为立即失败阻塞等待超时等待
插入add 抛异常、offer 返回 falseputoffer(e, timeout, unit)
取出remove 抛异常、poll 返回 nulltakepoll(timeout, unit)

服务请求线程通常优先带超时版本,避免无限等待。后台批处理可以使用 put/take,但仍要响应中断,让关闭流程可终止。

容量来自等待预算

容量不是越大越安全。粗略估算可以从 Little’s Law 开始:系统中的平均任务数约等于到达率乘平均停留时间。例如消费者 每秒处理 100 个任务,允许排队 2 秒,队列起点约为 200;随后用峰值、尾延迟和对象大小压测修正。

队列指标至少包含当前深度、容量使用率、等待时间、拒绝数、任务年龄和消费耗时。只监控线程池活跃数看不见陈旧任务。

公平性与吞吐

ArrayBlockingQueue 可选择公平锁,让等待较久的线程优先,但通常牺牲吞吐。LinkedBlockingQueue 若不传容量,默认上限 非常大,实际效果接近无界;在线服务应显式设置容量。

单机队列不提供进程崩溃后的持久性。任务不能丢失时使用数据库 Outbox 或消息系统,并同时设计幂等消费和重试上限。

下一步

阅读ThreadPoolExecutor 参数、拒绝与动态调节,把队列背压扩展到完整 任务执行器。


分享这篇文章:

上一篇
并发 Map、List 和 Set 的实现权衡
下一篇
ThreadPoolExecutor 参数、拒绝与动态调节