生产者消费者模式关于 Java 的三种实现

博客聚焦生产者和消费者问题,这是线程模型经典问题,生产者和消费者共用存储空间。设计实现时要注意同步、容器满时生产者暂停、容器空时消费者暂停等问题。还给出基于synchronized与wait/notify、ReentrantLock和Condition、信号量的多生产者多消费者具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要参考文章:https://juejin.im/entry/596343686fb9a06bbd6f888c


生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。

在设计实现该模式的时候,需要注意以下三个问题:

  1. 对容器取产品和添加产品时的同步问题,即任意时刻,取与添加是互斥的,且每一次取操作之间也是互斥的(添加操作也是如此)。

  2. 当容器满了之后,生产者需要暂时停止生产,直到容器非满

  3. 当容器空了之后,消费者需要暂时停止消费,直到容器非空

针对多个生产者与多个消费者的具体实现如下:


具体实现
(1)基于 synchronized 与 wait/notify
private static final int MAX_COUNT = 10;
private final Object lock = new Object();
private int count = 0;

class Producer implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
                synchronized (lock) {
                    // 多个生产者的情况下用 while 循环,单个用 if
                    while (count == MAX_COUNT) {
                        lock.wait();
                    }
                    ++count;
                    System.out.println("生产者产生了一个数据,当前总数:" + count);
                    
                    lock.notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Consumer implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(50);
                synchronized (lock) {
                    while (count < 1) {
                        lock.wait();
                    }
                    --count;
                    System.out.println("消费者消费了一个数据,当前总数:" + count);
                    lock.notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public static void main(String[] args) {
    synchronized实现 test = new synchronized实现();
    for (int i = 0; i < 10; i++) {
        new Thread(test.new Producer()).start();
    }
    for (int i = 0; i < 5; i++) {
        new Thread(test.new Consumer()).start();
    }
}

其中,synchronizedObject#wait() 都是基于对象的锁。

当通过 synchronized 持有了同步对象 A 的锁后,如果在同步代码中调用了 A.wait() 方法,则会释放 synchronized 占有的锁。

且对于多个生产者或者多个消费者的情况,要用 while() 循环,重新判断条件,避免逻辑混乱。

因为执行完 lock.wait() 之后,会进入下一轮循环,此时需要重新判断资源的情况。

(2)基于 ReentrantLock 和 Condition
private static final int MAX_COUNT = 10;

private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private int count = 0;

class Producer implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(50);
                lock.lock();
                
                while (count == MAX_COUNT) {
                    notFull.await();
                }
                ++count;
                System.out.println("生产者产生了一个数据,当前总数:" + count);
                
                // 唤醒所有消费者
                notEmpty.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}
class Consumer implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
                lock.lock();
                while (count < 1) {
                    notEmpty.await();
                }
                --count;
                System.out.println("消费者消费了一个数据,当前总数:" + count);
                
                // 唤醒所有生产者
                notFull.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
           		// 最后要注意手动释放锁
                lock.unlock();
            }
        }
    }
}
(3)基于信号量
private static final int MAX_COUNT = 10;
// 实现数据消耗互斥的信号量
private final Semaphore mutex = new Semaphore(1);
// 控制生产者的信号量
private final Semaphore notFull = new Semaphore(MAX_COUNT);
// 控制消费者的信号量
private final Semaphore notEmpty = new Semaphore(0);

private int count = 0;

class Producer implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(50);
                
                // 请求一个生产者信号量,如果当前信号量不足 10 个则堵塞(因为设置的 MAX_COUNT 为 10)
                notFull.acquire();
                mutex.acquire();
                
                ++count;
                System.out.println("生产者产生了一个数据,当前总数:" + count);
                
                mutex.release();
                // 设置消费者可以消费的信号量个数 +1
                notEmpty.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
                
                notEmpty.acquire();
                mutex.acquire();
                
                --count;
                System.out.println("消费者消费了一个数据,当前总数:" + count);
                
                mutex.release();
                notFull.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值