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. 三大价值:解耦、异步、限流
相关推荐
言乐615 分钟前
Python根据无法识别搜索词找出可能输入内容模型
开发语言·python·django·virtualenv·pygame
weixin_3077791320 分钟前
有限产能智能排产与动态重排智能体:从需求解构到技术实现
开发语言·人工智能·算法·架构
Ivanqhz25 分钟前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
kakakahahahaha29 分钟前
Windows C盘临时文件清理:%temp%、Windows Temp、Prefetch、Windows.old与hiberfil.sys处理指南
c语言·开发语言·windows
朝朝辞暮i35 分钟前
C++ 第 27 课:智能指针 shared_ptr
开发语言·c++·算法
沙漠之主1 小时前
C++编程教学设计资料:从入门到实战的完整课程方案
java·前端·c++
yl45301 小时前
硫酸泄露处理生产商怎么选才够专业
大数据·人工智能·python
朝朝辞暮i1 小时前
C++ 第 29 课:回调函数 Callback
开发语言·c++·算法
FPGA小徐1 小时前
PA1 基础设施(2)深度解析:NEMU 中 ITRACE、MTRACE、FTRACE、IRINGBUF、ETRACE、DIFFTEST 全解析
java·服务器·网络
笨笨饿1 小时前
140_AI新手村MCP与Skills是干嘛的
开发语言·人工智能·python·stm32·单片机·嵌入式硬件·物联网