新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java 阻塞队列——实现生产者消费者模型

发布时间:2026/9/27 5:23:25来源:尧图网络
Java 阻塞队列——实现生产者消费者模型
一、什么是生产者消费者模型生产者消费者Producer-Consumer是多线程里最经典的设计模型核心就是解耦、削峰、并行。 角色划分生产者负责产生数据/任务往队列里放消费者负责处理数据/任务从队列里取缓冲区队列生产者和消费者之间的中间容器作为“仓库”。生活化比喻包饺子。多人包饺子有的人负责擀皮生产者有的人负责包消费者擀好的饺子皮放在盘子缓冲区队列。盘子满了擀皮的暂停盘子空了包饺子的等待。模型三大核心作用解耦生产者和消费者不需要直接通信只依赖队列。生产者代码改动不会直接影响消费者。分布式系统中A服务生产消息放入队列B服务消费A、B互不感知。削峰流量缓冲突发大量请求时请求先放入队列消费者按自身能力慢慢处理不会瞬间压垮下游服务。举例服务A突增大量请求直接调用服务BB会被打崩中间加队列请求排队B匀速消费抵御流量尖刺。并行处理生产、消费两个动作可以并发执行提升整体吞吐量。生产者只管生产消费者只管消费互不阻塞。代价引入队列后带来额外开销队列占用内存多线程读写队列需要加锁带来锁竞争还要处理队列满、队列空、并发异常等问题代码复杂度上升。需要部署更多的设备生产环境也会更加复杂管理起来更麻烦二、Java中实现阻塞队列 BlockingQueue生产者消费者模型最推荐使用BlockingQueue阻塞队列JDK原生提供不用手写wait/notify线程安全。BlockingQueue特点队列满时生产者调用put()放入元素线程自动阻塞等待队列空时消费者调用take()取出元素线程自动阻塞等待常用实现类ArrayBlockingQueue数组实现有界队列初始化必须指定容量底层是数组。LinkedBlockingQueue链表实现可设置有界/无界。无界时风险极大生产速度持续大于消费元素无限堆积内存暴涨触发OOM。重点不要随便使用无界LinkedBlockingQueue生产环境很容易内存溢出。基础代码示例BlockingQueue版本import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class ProducerConsumerDemo { public static void main(String[] args) { // 缓冲区容量设为5 BlockingQueueString queue new LinkedBlockingQueue(5); // 生产者线程 Thread producer new Thread(() - { for (int i 1; i 10; i) { try { String data 任务- i; queue.put(data); // 队列满自动阻塞 System.out.println(生产者生产 data); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }, 生产者); // 消费者线程 Thread consumer new Thread(() - { while (true) { try { String task queue.take(); // 队列空自动阻塞 System.out.println(消费者处理 task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } }, 消费者); producer.start(); consumer.start(); } }三、底层原理wait notify 手写版本 虚假唤醒我们也可以不用BlockingQueue使用synchronized wait() notify()手写缓冲区。这里会遇到面试高频考点虚假唤醒。什么是虚假唤醒wait() 的线程在没有被notify唤醒的情况下也可能被操作系统唤醒。 所以不能用if判断队列状态必须用while循环。错误写法// ❌错误if只判断一次虚假唤醒后不会二次校验 if(队列是空){ wait(); }正确写法// ✅while循环唤醒之后再次循环检查条件 while(队列是空){ wait(); }原理线程被唤醒后会重新回到while条件判断。如果此时队列依旧为空会再次进入wait等待规避虚假唤醒带来的bug。wait/notify 手写简易生产者消费者class MyBlockingQueue { private String[] data null; // 队首 private int head 0; // 队尾 private int tail 0; // 元素个数 private int size 0; public MyBlockingQueue(int capacity) { data new String[capacity]; } public void put(String elem) throws InterruptedException { synchronized (this) { while (size data.length) { // 队列满了. 需要阻塞的 // return; this.wait(); } data[tail] elem; tail; if (tail data.length) { tail 0; } size; this.notify(); } } public String take() throws InterruptedException { synchronized (this) { while (size 0) { // 队列空了. 需要阻塞 // return null; this.wait(); } String ret data[head]; head; if (head data.length) { head 0; } size--; this.notify(); return ret; } } } public class Demo31 { public static void main(String[] args) { MyBlockingQueue queue new MyBlockingQueue(1000); Thread producer new Thread(() - { int n 0; while (true) { try { queue.put(n ); System.out.println(生产元素 n); n; } catch (InterruptedException e) { throw new RuntimeException(e); } } }); Thread consumer new Thread(() - { while (true) { String n null; try { n queue.take(); System.out.println(消费元素 n); Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); producer.start(); consumer.start(); } }注意优先使用notifyAll()而不是notify()。notify只随机唤醒一个线程容易出现信号丢失所有线程全部阻塞发生死锁。这里就两个线程互相唤醒所以使用notify()四、常见坑点总结面试高频无界队列OOM风险LinkedBlockingQueue不设置容量任务无限堆积内存耗尽。生产环境尽量用有界队列。虚假唤醒wait判断条件必须while不能if。notify 信号丢失推荐notifyAll避免线程永久等待。消费速度跟不上生产队列持续积压需要扩容消费者、优化消费逻辑、限流保护。中断异常处理BlockingQueue的put/take会抛出InterruptedException捕获后要恢复中断标记Thread.currentThread().interrupt()不要吞掉中断。五、Java ArrayList 与 LinkedList 区别高频考点特性ArrayListLinkedList底层结构动态 Object 数组内存连续双向链表内存分散随机访问 getO(1)O(n)尾部 addO (1)扩容时 O (n)O(1)中间插入 / 删除O (n)移动元素O (n)遍历找节点内存开销较小仅预留数组空间大每个节点存双指针遍历推荐普通 for / 迭代器迭代器 / 增强 for禁止 forget线程安全非线程安全非线程安全
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

嵌入式开发三步闭环:编译、烧录与仿真的原理与实践 2026/9/27 6:16:32

嵌入式开发三步闭环:编译、烧录与仿真的原理与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
I2S音频总线调试实战:从信号定义到逻辑分析仪波形定位 2026/9/27 6:16:32

I2S音频总线调试实战:从信号定义到逻辑分析仪波形定位

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
厦门市住宅建设办公室网站从零搭建防坑指南 2026/9/27 6:16:26

厦门市住宅建设办公室网站从零搭建防坑指南

厦门市住宅建设办公室网站从零搭建防坑指南 别信那些张口就要大几千的建站公司,找对路子,预算能省一半。很多项目负责人在搭建【厦门市住宅建设办公室网站】时,最头疼的不是技术,而是怕被销售忽悠,签了高价合同最后发现功能还没人家官网的一半好用。今天…

阅读更多 →
ADRV9009 no-OS工程移植与Xilinx SDK调试实战指南 2026/9/27 6:16:26

ADRV9009 no-OS工程移植与Xilinx SDK调试实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
使用session和cookie实现网站自动登录.net选哪家好 2026/9/27 6:16:19

使用session和cookie实现网站自动登录.net选哪家好

3步搞定自动登录:.net站点防掉线保姆级建站教程 网站做好了没人访问?别慌,先解决用户留不住的问题。很多老板以为流量是靠广告砸出来的,其实很多时候是登录体验太烂把人赶走了。用户填一次密码,下次还得填,这种体验在移动端尤其劝退。今天这篇…

阅读更多 →
商城类网站建设方案:5步搞定源码下载与设计落地 2026/9/27 6:16:13

商城类网站建设方案:5步搞定源码下载与设计落地

商城类网站建设方案:5步搞定源码下载与设计落地 自己不会代码,却想搞个像样的商城网站?别急着去网上乱搜那些过时的模板,或者花大几千买套用不了的“源码下载”。很多老板一上来就问:“有没有那种后台点几下就能出站的?” 答案是:有,但坑更多。…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉