You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java单生产者双消费者问题:仅第二个消费者获取数据的解决方法

解决单生产者双消费者的线程唤醒问题

看起来你的问题核心在于线程唤醒机制的使用不当,导致只有一个消费者能收到数据通知。咱们先拆解问题原因,再给出修复方案。

常见问题根源

你当前的代码大概率犯了这两个典型错误:

  1. 用notify()替代了notifyAll():notify()只会随机唤醒一个等待在锁对象上的线程,当有两个消费者时,很可能每次都只唤醒同一个,另一个线程会一直处于等待状态。
  2. 消费者用if判断等待条件而非while:即使线程被唤醒,也可能是"虚假唤醒"(比如其他线程的notify误触发),如果不用循环重新检查条件,线程会直接跳过等待逻辑,导致数据处理出错或拿不到新数据。

修复后的完整代码示例

下面是修正后的ProdConsumer类和主方法,我会标注关键修改点:

class ProdConsumer {
    // 共享数据容器,这里用一个简单的字符串模拟
    private String data;
    // 标记是否有可消费的数据
    private boolean hasData = false;

    // 生产者方法:生成数据
    public synchronized void produce(String newData) throws InterruptedException {
        // 循环等待:如果已有数据,等待消费者取走
        while (hasData) {
            wait();
        }
        // 生成数据
        this.data = newData;
        System.out.println("生产者生成数据: " + data);
        hasData = true;
        // 关键修改:用notifyAll()唤醒所有等待的消费者线程
        notifyAll();
    }

    // 消费者方法:获取并处理数据
    public synchronized String consume(String consumerName) throws InterruptedException {
        // 循环等待:如果没有数据,等待生产者生成
        while (!hasData) {
            wait();
        }
        // 消费数据
        String consumedData = this.data;
        System.out.println(consumerName + " 消费数据: " + consumedData);
        hasData = false;
        // 唤醒生产者(如果生产者在等待的话)
        notifyAll();
        return consumedData;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        final ProdConsumer pc = new ProdConsumer();

        // 生产者线程:循环生成数据
        Thread producerThread = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    pc.produce("数据-" + i);
                    // 模拟生产耗时
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "生产者线程");

        // 第一个消费者线程
        Thread consumer1 = new Thread(() -> {
            try {
                for (int i = 1; i <= 3; i++) {
                    pc.consume("消费者1");
                    Thread.sleep(800);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "消费者1");

        // 第二个消费者线程
        Thread consumer2 = new Thread(() -> {
            try {
                for (int i = 1; i <= 2; i++) {
                    pc.consume("消费者2");
                    Thread.sleep(600);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "消费者2");

        // 启动所有线程
        producerThread.start();
        consumer1.start();
        consumer2.start();

        // 等待所有线程结束
        producerThread.join();
        consumer1.join();
        consumer2.join();
    }
}

关键修改说明

  • notifyAll()替代notify():每次生产完数据后,唤醒所有等待的消费者线程,确保两个消费者都有机会竞争锁并获取数据。同理,消费者消费完后也用notifyAll()唤醒生产者(如果生产者在等待的话)。
  • 用while循环判断等待条件:无论是生产者还是消费者,都用while代替if来检查条件。这样即使线程被虚假唤醒,也会重新检查状态,确保只有当条件满足时才继续执行。
  • 明确线程命名:给线程命名可以让控制台输出更清晰,方便排查哪个线程在处理数据。

运行效果示例

执行后你会看到类似这样的输出(顺序可能因线程调度略有不同):

生产者生成数据: 数据-1
消费者1 消费数据: 数据-1
生产者生成数据: 数据-2
消费者2 消费数据: 数据-2
生产者生成数据: 数据-3
消费者1 消费数据: 数据-3
生产者生成数据: 数据-4
消费者2 消费数据: 数据-4
生产者生成数据: 数据-5
消费者1 消费数据: 数据-5

这样两个消费者就能交替获取生产者生成的数据了。

内容的提问来源于stack exchange,提问作者just ME

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:24:48