blockingqueue的核心作用是简化线程安全的队列操作,自动处理阻塞与唤醒;arrayblockingqueue有界、基于数组,linkedblockingqueue默认无界、基于链表;生产者应使用带超时的offer避免永久阻塞,消费者需用take或毒丸对象实现优雅停机,多消费者须避免poll空转,线程停机需配合中断或毒丸并捕获interruptedexception。

BlockingQueue 的核心作用不是“实现模式”,而是解决线程安全的队列操作
直接用 BlockingQueue 实现生产者-消费者,本质是把“加锁+等待+唤醒”的繁琐逻辑交给 JDK 自带的阻塞队列完成。它不负责定义业务逻辑,只保证:生产者调用 put() 时若队列满就自动阻塞,消费者调用 take() 时若队列空也自动阻塞——不需要手写 wait()/notify() 或 Lock/Condition。
选对实现类:ArrayBlockingQueue 和 LinkedBlockingQueue 的关键差异
两者都实现了 BlockingQueue,但行为不同,选错会导致吞吐量骤降或 OOM:
-
ArrayBlockingQueue是有界、基于数组、公平锁可选(构造时传true),适合对内存占用敏感、需严格控流的场景 -
LinkedBlockingQueue默认无界(实际是Integer.MAX_VALUE),基于链表,吞吐通常更高;但若生产者远快于消费者,可能吃光堆内存 - 如果明确要“有界且高吞吐”,建议显式指定容量并用
LinkedBlockingQueue(int capacity),避免默认无界陷阱
生产者和消费者的典型写法与常见崩溃点
错误常出现在异常处理和退出逻辑上,不是功能写不对,而是没兜住边界情况:
- 生产者别在循环里无条件
queue.put(item)—— 若下游彻底卡死,这里会永久阻塞,应配合超时:queue.offer(item, 3, TimeUnit.SECONDS)并检查返回值 - 消费者用
take()没问题,但若想优雅停机,不能靠“队列变空”判断结束,得加中断或毒丸对象(poison pill);例如约定null或特殊END对象作为终止信号 - 多消费者共用一个队列时,
poll()+if (item != null)是错的——它不阻塞,会疯狂空转 CPU;必须用take()或带超时的poll(long, TimeUnit)
别忽略线程生命周期管理:shutdown() 不等于 stop()
ExecutorService 提交的生产者/消费者任务,不能只靠 shutdown() 就认为队列清空了:
-
shutdown()只是拒绝新任务,已提交的还在跑;必须跟awaitTermination()等待完成 - 若消费者还在
take()阻塞,而你已经 shutdown,它不会自动醒来——得先queue.put(poisonPill)或中断线程 - 中断线程后,
take()会抛InterruptedException,必须捕获并清理资源,否则线程可能残留
BlockingQueue 写生产者-消费者,最难的从来不是“怎么放怎么取”,而是“怎么让它们一起停下来,还不丢数据、不抛异常、不卡死”。










