题目描述如下:
有一家生产奶酪的厂家,每天需要生产100000份奶酪卖给超市,通过一辆送货车发货,送货车辆每次送100份。
厂家有一个容量为1000份的冷库,用于奶酪保鲜,生产的奶酪需要先存放在冷库,运输车辆从冷库取货。
厂家有三条生产线,分别是牛奶供应生产线,发酵剂制作生产线,奶酪生产线。生产每份奶酪需要2份牛奶和1份发酵剂。
请设计生产系统。
根据以下要求,我们可以考虑这么几个方面
牛奶生产和发酵剂生产作为生产者,奶酪生产作为消费者
奶酪生产作为生产者,货车发车作为消费者
牛奶存储和发酵剂存储以及奶酪存储,使用3个阻塞队列暂存
货车装车从奶酪队列里每取一百次发一次车
一共4个生产者,2个消费者,3个阻塞队列
下面是代码实现
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 生产奶酪
*
* @author since
* @date 2020-11-02 16:04
**/
public class Cheese {
/**
* 每天生产奶酪的总量
*/
private static final Integer TOTAL_PRODUCTION = 100000;
/**
* 牛奶的数量-200000/天
*/
private static final AtomicInteger MILK_SIZE = new AtomicInteger(0);
/**
* 发酵剂的数量-100000/天
*/
private static final AtomicInteger STARTER_SIZE = new AtomicInteger(0);
/**
* 奶酪的数量-100000/天
*/
private static final AtomicInteger CHEESE_SIZE = new AtomicInteger(0);
/**
* 取奶酪的次数
*/
private static final AtomicInteger CHEESE_TAKE_TIMES = new AtomicInteger(0);
/**
* 阻塞队列,用于存放奶酪,生产者是牛奶和发酵剂,消费者是货车发货,每次发100份
*/
private static final BlockingQueue CHEESE_QUEUE = new LinkedBlockingQueue<>(1000);
/**
* 阻塞队列,用于存放生产的牛奶,生产者是牛奶,消费者是奶酪生产
*/
private static final BlockingQueue MILK_QUEUE = new LinkedBlockingQueue<>(2);
/**
* 阻塞队列,用于存放生产的发酵剂,生产者是发酵剂,消费者是奶酪生产
*/
private static final BlockingQueue STARTER_QUEUE = new LinkedBlockingQueue<>(1);
/**
* 奶酪生产线程池
*/
private static final ThreadPoolExecutor CHEESE_PRODUCER = new ThreadPoolExecutor(1, 2, 1, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("cheese-producer-%d").build(), (r, executor) -> System.out.println("Cheese Reject Task"));
/**
* 发酵剂生产线程池
*/
private static final ThreadPoolExecutor STARTER_PRODUCER = new ThreadPoolExecutor(1, 2, 1, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("starter-producer-%d").build(), (r, executor) -> System.out.println("Starter Reject Task"));
/**
* 牛奶生产线程池
*/
private static final ThreadPoolExecutor MILK_PRODUCER = new ThreadPoolExecutor(1, 2, 1, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("milk-producer-%d").build(), (r, executor) -> System.out.println("Milk Reject Task"));
/**
* 货车发货线程池
*/
private static final ThreadPoolExecutor TRUCKING = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("trucking-%d").build(), (r, executor) -> System.out.println("Trunking Reject Task"));
/**
* 奶酪生产线程
*/
static class CheeseProducer implements Runnable {
@Override
public void run() {
while (CHEESE_SIZE.get() < TOTAL_PRODUCTION) {
try {
//发酵剂取1
STARTER_QUEUE.take();
//System.out.println("生产奶酪,发酵剂取1");
//牛奶取2
MILK_QUEUE.take();
MILK_QUEUE.take();
//System.out.println("生产奶酪,发酵剂取2");
//奶酪生产1
CHEESE_QUEUE.put(1);
//System.out.println("生产1份奶酪");
//生产数量加1
int i = CHEESE_SIZE.incrementAndGet();
if (CHEESE_SIZE.get() == TOTAL_PRODUCTION) {
System.out.println("本日奶酪已生产完,共" + i + "份");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 发酵剂生产线程
*/
static class StarterProducer implements Runnable {
@Override
public void run() {
while (STARTER_SIZE.get() < TOTAL_PRODUCTION) {
try {
STARTER_QUEUE.put(1);
//System.out.println("生产1份发酵剂");
//发酵剂+1
int i = STARTER_SIZE.incrementAndGet();
if (STARTER_SIZE.get() == TOTAL_PRODUCTION) {
System.out.println("本日发酵剂已生产完,共" + i + "份");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 牛奶生产线程
*/
static class MilkProducer implements Runnable {
@Override
public void run() {
while (MILK_SIZE.get() < TOTAL_PRODUCTION * 2) {
try {
MILK_QUEUE.put(1);
//System.out.println("生产1份牛奶");
//牛奶数量+1
int i = MILK_SIZE.incrementAndGet();
if (MILK_SIZE.get() == TOTAL_PRODUCTION * 2) {
System.out.println("本日牛奶已生产完,共" + i + "份");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 货车发货线程
*/
static class Trucking implements Runnable {
@Override
public void run() {
while (true) {
try {
//取奶酪装车准备发货
CHEESE_QUEUE.take();
int i1 = CHEESE_TAKE_TIMES.incrementAndGet();
if (i1 % 100 == 0) {
System.out.println("货车装满100份,发车");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
CHEESE_PRODUCER.execute(new CheeseProducer());
STARTER_PRODUCER.execute(new StarterProducer());
MILK_PRODUCER.execute(new MilkProducer());
TRUCKING.execute(new Trucking());
while (true) {
if (CHEESE_SIZE.get() == TOTAL_PRODUCTION) {
MILK_PRODUCER.shutdown();
STARTER_PRODUCER.shutdown();
CHEESE_PRODUCER.shutdown();
TRUCKING.shutdown();
System.exit(0);
}
}
}
}
如上代码就是一个典型的生产者消费者代码,而且都是单线程生产和消费。
1份奶酪需要2份牛奶和1份发酵剂,确定了2个生产者和一个消费者。
货车从冷库里取奶酪,奶酪需要放在冷库里,确定了一个生产者和一个消费者
题目要求的所有的生产和消费行为都是单线程的
我自己在实现的时候,从冷库取奶酪的时候,使用了5个线程从冷库里取奶酪,这样会导致在计算取多少次的时候,会导致数据打印出来会乱序。
题目要求的是每100份奶酪发一次车,所以需要在冷库里取100次,下面如果不加锁,会有问题,导致乱序
所以货车如果改成多线程,下面这几行代码需要加锁保证顺序
CHEESE_QUEUE.take();
int i1 = CHEESE_TAKE_TIMES.incrementAndGet();
if (i1 % 100 == 0) {
System.out.println("货车装满100份,发车");
}