登录
首页 >  文章 >  java教程

Java生产者消费者模型实现解析

时间:2026-02-07 23:54:46 429浏览 收藏

欢迎各位小伙伴来到golang学习网,相聚于此都是缘哈哈哈!今天我给大家带来《Java生产者消费者模型实现详解》,这篇文章主要讲到等等知识,如果你对文章相关的知识非常感兴趣或者正在自学,都可以关注我,我会持续更新相关文章!当然,有什么建议也欢迎在评论留言提出!一起学习!

最稳妥的方式是使用BlockingQueue而非手写wait/notify,因其天然线程安全、阻塞语义明确、边界处理完整;手写易出现唤醒丢失、虚假唤醒、未用while循环检查条件及锁粒度不合理等问题。

在Java中生产者消费者模型如何实现_Java经典并发模型解析

Java里实现生产者消费者模型,最稳妥的方式不是手写 wait/notify,而是用 BlockingQueue——它天然线程安全、阻塞语义明确、边界处理完整。

为什么别直接用 wait/notify 手写?

手写容易漏掉几个关键点:唤醒丢失(notify 早于 wait)、虚假唤醒(spurious wakeup)、未在循环中检查条件、锁粒度不合理。哪怕代码看着“能跑”,在高并发或压力下极易出现死锁、数据丢失或无限等待。

常见错误现象包括:

  • 生产者一直阻塞,消费者取不到新数据(notify 被忽略)
  • 队列为空时消费者仍尝试取值(没用 while 循环重检条件)
  • 多个生产者/消费者共用同一把锁,吞吐量骤降

BlockingQueue 的三种典型用法

选哪种取决于场景对容量、公平性、响应性的要求:

  • ArrayBlockingQueue:有界、基于数组、构造时必须指定容量;适合内存敏感、需硬性限流的场景(如日志缓冲区)
  • LinkedBlockingQueue:默认无界(实际是 Integer.MAX_VALUE),但可传参设界;吞吐通常更高,但无界时可能 OOM
  • SynchronousQueue:不存储元素,每个 put 必须配一个 take;适合任务交接型场景(如线程池的 DirectHandoff

示例:用 ArrayBlockingQueue 实现基础模型

BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
// 生产者
new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        try {
            queue.put("item-" + i); // 阻塞直到有空位
            System.out.println("produced: item-" + i);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}).start();

// 消费者
new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        try {
            String item = queue.take(); // 阻塞直到有数据
            System.out.println("consumed: " + item);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}).start();

如果必须用 synchronized + wait/notify,怎么写才不出错?

仅当需要自定义等待逻辑(比如超时、多条件组合)且无法用 BlockingQueue 替代时才考虑。核心守则只有三条:

  • 必须在 synchronized 块内调用 wait()notify()
  • 必须用 while 循环检查条件,不能用 if
  • 所有修改共享状态的操作,和所有 wait() 的判断条件,必须使用同一把锁

错误写法:if (queue.isEmpty()) wait(); → 可能虚假唤醒后直接取空队列

正确写法:while (queue.isEmpty()) wait();

真正难的不是写出来,而是想清楚「谁负责唤醒」「唤醒是否及时」「中断是否被正确传播」——这些细节在 BlockingQueue 里已被反复验证过,自己重造轮子时最容易在日志里看到 IllegalMonitorStateException 或线程卡死,却查不出哪一行锁没对上。

以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于文章的相关知识,也可关注golang学习网公众号。

前往漫画官网入口并下载 ➜
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>