主要参考文章:https://juejin.im/entry/596343686fb9a06bbd6f888c
生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。
在设计实现该模式的时候,需要注意以下三个问题:
-
对容器取产品和添加产品时的同步问题,即任意时刻,取与添加是互斥的,且每一次取操作之间也是互斥的(添加操作也是如此)。
-
当容器满了之后,生产者需要暂时停止生产,直到容器非满
-
当容器空了之后,消费者需要暂时停止消费,直到容器非空
针对多个生产者与多个消费者的具体实现如下:
具体实现
(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();
}
}
其中,synchronized
和 Object#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();
}
}
}
}