Java 生产者-消费者模型详解

生产者-消费者模型是多线程编程中最经典的同步协作模型 ,核心解决:生产者生产数据→放入缓冲区→消费者从缓冲区取数据 ,实现解耦、异步、限流,避免生产速度和消费速度不匹配导致的数据丢失/阻塞。

一、核心概念

  1. 生产者线程:负责生产数据,放入共享缓冲区
  2. 消费者线程:负责从缓冲区取出数据,消费数据
  3. 共享缓冲区:线程安全的容器(队列/列表),是协作核心
  4. 同步机制:必须保证缓冲区空时消费者等待、缓冲区满时生产者等待;生产/消费后唤醒对方

二、Java 实现方式(3种常用方案)

方式1:Object wait/notify 实现(基础版)

利用 Java 内置的 wait()notifyAll() 实现线程等待/唤醒,最基础、面试必考

java 复制代码
import java.util.LinkedList;
import java.util.Queue;

// 共享缓冲区
class SharedBuffer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity; // 缓冲区最大容量

    public SharedBuffer(int capacity) {
        this.capacity = capacity;
    }

    // 生产者:放入数据
    public synchronized void produce(int value) throws InterruptedException {
        // 缓冲区满,生产者等待
        while (queue.size() == capacity) {
            System.out.println("缓冲区已满,生产者等待...");
            wait();
        }
        queue.add(value);
        System.out.println("生产者生产:" + value + ",缓冲区大小:" + queue.size());
        notifyAll(); // 唤醒等待的消费者
    }

    // 消费者:取出数据
    public synchronized void consume() throws InterruptedException {
        // 缓冲区空,消费者等待
        while (queue.isEmpty()) {
            System.out.println("缓冲区为空,消费者等待...");
            wait();
        }
        int value = queue.poll();
        System.out.println("消费者消费:" + value + ",缓冲区大小:" + queue.size());
        notifyAll(); // 唤醒等待的生产者
    }
}

// 生产者线程
class Producer implements Runnable {
    private final SharedBuffer buffer;

    public Producer(SharedBuffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        int value = 1;
        try {
            while (true) { // 持续生产
                buffer.produce(value++);
                Thread.sleep(500); // 模拟生产耗时
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

// 消费者线程
class Consumer implements Runnable {
    private final SharedBuffer buffer;

    public Consumer(SharedBuffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try {
            while (true) { // 持续消费
                buffer.consume();
                Thread.sleep(1000); // 模拟消费耗时
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

// 测试主类
public class ProducerConsumerDemo {
    public static void main(String[] args) {
        SharedBuffer buffer = new SharedBuffer(3); // 缓冲区容量3
        new Thread(new Producer(buffer), "生产者").start();
        new Thread(new Consumer(buffer), "消费者").start();
    }
}

关键知识点

  • 必须用 while 循环判断缓冲区状态(防止虚假唤醒)
  • wait() 会释放锁,sleep() 不会释放锁
  • notifyAll() 唤醒所有等待线程,比 notify() 更安全

方式2:Lock/Condition 实现(进阶版)

ReentrantLock + Condition 替代 synchronized支持精准唤醒生产者/消费者,效率更高。

java 复制代码
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class SharedBuffer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();  // 生产者等待条件
    private final Condition notEmpty = lock.newCondition(); // 消费者等待条件

    public SharedBuffer(int capacity) {
        this.capacity = capacity;
    }

    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                notFull.await(); // 生产者等待
            }
            queue.add(value);
            System.out.println("生产:" + value + ",缓冲区:" + queue.size());
            notEmpty.signal(); // 精准唤醒消费者
        } finally {
            lock.unlock(); // 必须手动释放锁
        }
    }

    public void consume() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await(); // 消费者等待
            }
            int value = queue.poll();
            System.out.println("消费:" + value + ",缓冲区:" + queue.size());
            notFull.signal(); // 精准唤醒生产者
        } finally {
            lock.unlock();
        }
    }
}

优势

  • 两个 Condition 分离等待队列,避免无效唤醒
  • 手动加锁/释放锁,灵活性更高

方式3:BlockingQueue 实现(推荐,开发首选)

Java 并发包提供的线程安全阻塞队列ArrayBlockingQueue/LinkedBlockingQueue),内置阻塞机制,代码极简。

java 复制代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

// 生产者
class Producer implements Runnable {
    private final BlockingQueue<Integer> queue;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        int value = 1;
        try {
            while (true) {
                queue.put(value++); // 队列满则自动阻塞
                System.out.println("生产:" + (value-1));
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

// 消费者
class Consumer implements Runnable {
    private final BlockingQueue<Integer> queue;

    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                int value = queue.take(); // 队列空则自动阻塞
                System.out.println("消费:" + value);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

// 测试
public class BlockingQueueDemo {
    public static void main(String[] args) {
        // 容量为3的阻塞队列
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
        new Thread(new Producer(queue)).start();
        new Thread(new Consumer(queue)).start();
    }
}

核心优势

  • 无需手动写同步、等待、唤醒逻辑
  • 开箱即用,工业级稳定,实际开发首选
  • put()/take() 自带阻塞,offer()/poll() 非阻塞

三、三种方案对比

实现方式 优点 缺点 适用场景
Object wait/notify 基础、面试必考 代码繁琐、易出错、效率低 学习、面试
Lock/Condition 精准唤醒、高效 代码稍复杂 高级并发场景
BlockingQueue 极简、安全、高效 封装度高,底层原理隐藏 生产环境、实际开发

四、核心作用

  1. 解耦:生产者和消费者不直接通信,通过缓冲区交互
  2. 异步:生产和消费并发执行,无需同步等待
  3. 削峰限流:缓冲区平衡生产/消费速度,避免系统崩溃

总结

  1. 生产者-消费者核心是共享缓冲区 + 线程同步
  2. 基础面试用 wait/notify,实际开发用 BlockingQueue
  3. 必须处理线程安全虚假唤醒(用while循环)
  4. 三大价值:解耦、异步、限流
相关推荐
en.en..3 分钟前
TCP/UDP 收发流程(网络字节序---函数讲解)
开发语言·php
vx-程序开发8 分钟前
【毕设分享】springboot废品上门回收微信小程序---附源码94491
java·spring boot·毕业设计·源码·课程设计·毕设·大作业
宸津-代码粉碎机9 分钟前
Spring AI 高危CVE漏洞深度复盘|生产禁跑版本汇总+临时防御+修复方案
java·大数据·人工智能·python·spring
名字还没想好☜15 分钟前
Go 用 json.Decoder 流式解析大 JSON:边读边处理不爆内存
开发语言·后端·golang·go·json
xiangyun6118 分钟前
【408数据结构 03】线性表与顺序表:C++手写SeqList
开发语言·数据结构·c++
Lsir10110_33 分钟前
从按钮“点不动“讲起——深入理解 Qt 信号槽机制
开发语言·qt·信号处理
君科程序定做35 分钟前
用 IDL 处理高分一号(GF-1 / GF-1B/C/D)数据
c语言·开发语言
敲代码的嘎仔38 分钟前
用 Redis 合并写 + DelayQueue 延迟检测,把视频播放进度写库频率降到 1/60
java·开发语言·数据库·redis·缓存·音视频·高并发
承渊政道1 小时前
Python IDLE鸿蒙PC适配全记录:用 ArkUI 重建编辑、运行、Shell 与基础调试闭环
python·microsoft·harmonyos·鸿蒙系统·pc端
zjh9005301 小时前
Kotlin黑科技:空安全与Java兼容性深度解析
python