1、引言
生产者消费者是一个古老而又经典的问题,虽然现在已经有很多成熟的解决方案,但是里面的思想仍然值得我们去学习,去思考。
主要是在高并发场景下,我们是如何保证生产者在生产商品的时候是顺序向仓库里面添加商品的,什么时候仓库商品已满,停止生产商品,消费者是如何进行消费商品的,在什么条件下才能去消费商品,以及每次消费商品的数量能否保证,今天,我们就来讲讲如何实现一个较为完美的解决方案,
2、解决方案
思想:生产者每次生产商品都默认把商品放进仓库,当仓库满时则停止生产,进入等待状态,等待消费者消费商品,如果没有满,则在生产完商品时通知消费者去消费。消费者每次消费商品都默认从仓库中去取,判断仓库中是否有足够的库存容量,如果有,就进行取出消费,如果没有,就进行等待,知道生产者生产完产品发出通知。好了,前面就是我们大概的思路,我们现在把生产者,消费者、仓库三个类提取出来,开始编写我们的代码了。
1、消费者代码
package conditionTestManyToMany;
/**
* 消费者类继承线程类thread
*/
public class Consumer extends Thread{
//每次消费的产品数量
private int num;
//仓库
private Storage storage;
//构造函数,设置仓库
public Consumer(Storage storage){
this.storage = storage;
}
//线程函数
@Override
public void run(){
consume(num);
}
public void consume(int num){
storage.consume(num);
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Storage getStorage() {
return storage;
}
public void setStorage(Storage storage) {
this.storage = storage;
}
}
2、生产者代码
package conditionTestManyToMany;
/**
* 生产者类继承线程类Thread
* @author Archer
*/
public class Producer extends Thread{
//每次生产的产品数量
private int num;
//所在放置的仓库
private Storage storage;
public Producer(Storage storage){
this.storage = storage;
}
//线程函数
@Override
public void run(){
produce(num);
}
//调用仓库的Storage的生产函数
public void produce(int num){
storage.produce(num);
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Storage getStorage() {
return storage;
}
public void setStorage(Storage storage) {
this.storage = storage;
}
}
3、仓库代码
package conditionTestManyToMany;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Storage {
//仓库最大容量
private final int MAX_SIZE = 100;
//仓库存储的载体
private LinkedList<Object> list = new LinkedList<>();
//锁
private final Lock lock = new ReentrantLock();
//仓库满的条件变量
private final Condition full = lock.newCondition();
//仓库空的条件变量
private final Condition empty = lock.newCondition();
//生产num个产品
public void produce(int num){
//获得锁
lock.lock();
//仓库已满,等待消费者消费
while(list.size() + num > MAX_SIZE){
System.out.println("【要生产的产品数量】:" + num + "/t【库存量】:" + list.size()
+ "/t暂时不能执行生产任务!");
try{
full.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//生产num个产品
for(int i = 0;i < num;i++){
list.add(new Object());
}
System.out.println("【已经生产产品数】:" + num + "/t【现仓储量为】:" + list.size());
full.signalAll();
empty.signalAll();
lock.unlock();
}
public void consume(int num){
lock.lock();
//如果库存数量少于消费的产品数量,则进行等待
while(list.size() < num){
System.out.println("【要消费的产品数量】:" + num + "/t【库存量】:" + list.size()
+ "/t暂时不能执行生产任务!");
try{
empty.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//消费条件满足的情况下消费num个产品
for(int i = 0;i < num;i++){
list.remove();
}
System.out.println("【已经消费产品数】:" + num + "/t【现仓储量为】:" + list.size());
full.signalAll();
empty.signalAll();
lock.unlock();
}
public LinkedList<Object> getList() {
return list;
}
public void setList(LinkedList<Object> list) {
this.list = list;
}
public Lock getLock() {
return lock;
}
public int getMAX_SIZE() {
return MAX_SIZE;
}
}
4、测试类
package conditionTestManyToMany;
public class Test {
public static void main(String[] args) {
//仓库对象
Storage storage = new Storage();
//生产者对象
Producer p1 = new Producer(storage);
Producer p2 = new Producer(storage);
Producer p3 = new Producer(storage);
Producer p4 = new Producer(storage);
Producer p5 = new Producer(storage);
p1.setNum(10);
p2.setNum(15);
p3.setNum(5);
p4.setNum(20);
p5.setNum(90);
Consumer c1 = new Consumer(storage);
Consumer c2 = new Consumer(storage);
Consumer c3 = new Consumer(storage);
c1.setNum(20);
c2.setNum(30);
c3.setNum(50);
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
c1.start();
c2.start();
c3.start();
}
}
5、结果
【已经生产产品数】:10/t【现仓储量为】:10
【已经生产产品数】:15/t【现仓储量为】:25
【已经生产产品数】:5/t【现仓储量为】:30
【已经生产产品数】:20/t【现仓储量为】:50
【要生产的产品数量】:90/t【库存量】:50/t暂时不能执行生产任务!
【已经消费产品数】:20/t【现仓储量为】:30
【要生产的产品数量】:90/t【库存量】:30/t暂时不能执行生产任务!
【已经消费产品数】:30/t【现仓储量为】:0
【已经生产产品数】:90/t【现仓储量为】:90
【已经消费产品数】:50/t【现仓储量为】:40
Process finished with exit code 0