Java中用ArrayBlockingQueue和LinkedBlockingQueue实现生产者消费者模型,核心是利用其线程安全与阻塞特性:生产者调用put()(满则阻塞),消费者调用take()(空则阻塞),无需手动加锁或wait/notify。

Java 中用 ArrayBlockingQueue 和 LinkedBlockingQueue 实现生产者消费者模型,核心是利用它们线程安全、支持阻塞的特性——生产者调用 put()(满则阻塞),消费者调用 take()(空则阻塞),无需手动加锁或 wait/notify。
区别与选型要点
两者都是 BlockingQueue 接口的实现,但底层机制不同:
- ArrayBlockingQueue:基于固定大小的数组,构造时必须指定容量,有界队列,线程安全,使用单一可重入锁(所有操作共用一把锁)
-
LinkedBlockingQueue:基于链表,默认容量为
Integer.MAX_VALUE(即近乎无界),也可指定容量变为有界;内部使用**两把锁**(takeLock和putLock),读写操作可并发,吞吐量通常更高
简单场景(如固定缓冲区大小、内存敏感)优先选 ArrayBlockingQueue;高并发、吞吐优先、允许动态扩容倾向选 LinkedBlockingQueue(但注意无界可能导致 OOM)。
基础实现:一个生产者 + 一个消费者
以 ArrayBlockingQueue 为例,代码简洁清晰:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
// 生产者线程
new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
String item = "msg-" + i;
System.out.println("生产: " + item);
queue.put(item); // 满则阻塞
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
String item = queue.take(); // 空则阻塞
System.out.println("消费: " + item);
Thread.sleep(150);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
LinkedBlockingQueue 替换构造即可:new LinkedBlockingQueue<>(10)(显式设界)或 new LinkedBlockingQueue<>()(默认无界)。
多生产者多消费者场景注意事项
两种队列本身线程安全,多个生产者/消费者可直接共享同一实例,但需注意:
- 避免在循环中频繁调用
size()判断是否为空/满——它不是 O(1),且结果瞬时失效;应依赖take()/put()的阻塞语义 - 若需优雅关闭(如处理完已有任务后退出),可用
poll(timeout, unit)替代take(),配合标志位判断是否终止 - 异常处理要恢复中断状态(
Thread.currentThread().interrupt()),确保上层能感知中断意图
实际应用中的小技巧
提升健壮性和可维护性:
- 用
offer(e, timeout, unit)和poll(timeout, unit)实现带超时的非阻塞尝试,适合响应式或降级逻辑 - 对关键业务消息,可在生产前校验(如非 null)、消费后确认(如落库成功再 take),避免数据丢失
- 监控队列水位:定期打印
queue.size()和queue.remainingCapacity(),辅助判断负载和瓶颈

















