模拟场景:汉堡店有三个厨师,每个厨师3秒做一个汉堡。有6个顾客陆续到来,顾客2秒吃完一个汉堡,每个顾客需要吃5个汉堡才能吃饱。
根据命题,做出了第一版(存在报错,不要复制),一个厨师一个顾客的场景:
package com.lph.Test;
public class Test1 {
public static void main(String[] args) {
//餐厅中只有一个取餐台
Cabinet cabinet = new Cabinet();
Cooker cooker = new Cooker(cabinet);
Customer customer = new Customer(cabinet);
Thread cookerThread = new Thread(cooker);
cookerThread.setName("0号厨师");
Thread customerThread = new Thread(customer);
customerThread.setName("零号顾客");
cookerThread.start();
customerThread.start();
}
}
//汉堡类
class Hamburger{
String id; //编号
Hamburger(String id){
this.id = id;
}
@Override
public String toString() {
return "Hamburger{id='" + id + '}';
}
}
//取餐台类
class Cabinet{
int index = 0;
//取餐台中最多存放10个汉堡
Hamburger[] foods = new Hamburger[10];
//厨师做好汉堡放入取餐台
public void push(Hamburger hamburger){
System.out.println(Thread.currentThread().getName() + "制作了汉堡" + hamburger);
foods[index] = hamburger;
index++;
}
//顾客从取餐台取走汉堡
public Hamburger pop(){
index--;
System.out.println(Thread.currentThread().getName() + "吃掉了汉堡" + foods[index]);
return foods[index];
}
}
//厨师类
class Cooker implements Runnable{
Cabinet cabinet = null;
Cooker(Cabinet cabinet){
this.cabinet = cabinet;
}
@Override
public void run() {
int i = 0;
while (true){
Hamburger hamburger = new Hamburger(Thread.currentThread().getName() + i++);
cabinet.push(hamburger);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//顾客类
class Customer implements Runnable{
Cabinet cabinet = null;
Customer(Cabinet cabinet){
this.cabinet = cabinet;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "进入了餐厅");
for(int i = 0; i < 5; i++){
cabinet.pop();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "吃饱并离开了餐厅");
}
}
第一版直译了命题,问题是两个线程同时开始执行,在index++还未执行的时候,先执行了index–,也就是厨师做好了汉堡还没来的及放到取餐台里顾客就着急的去取餐,导致了错误。正确的流程是取餐台空的时候,顾客等着厨师做,取餐台满的时候,厨师等着顾客吃。直接的结论,对取餐台的push和pop进行修改。
第二版(一步步修改,当前依然存在问题,不要复制):
//厨师做好汉堡放入取餐台
public synchronized void push(Hamburger hamburger){
if (index == 10){
System.out.println(Thread.currentThread().getName() + "休息了");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "制作了汉堡" + hamburger);
foods[index] = hamburger;
index++;
}
//顾客从取餐台取走汉堡
public synchronized Hamburger pop(){
if (index == 0){
System.out.println(Thread.currentThread().getName() + "在等餐");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
System.out.println(Thread.currentThread().getName() + "吃掉了汉堡" + foods[index]);
return foods[index];
}
第二版首先对两个方法加上了关键字synchronized,变成了同步方法。这样就可以保证厨师做好汉堡往取餐台走的过程中顾客不会抢险一步,或者顾客在取餐时厨师抢先了一步,导致顾客取到了别人的汉堡。两个if判断使厨师不会往满了的取餐台里硬塞,顾客不会对着空的取餐台发呆。
但运行结果表示零号顾客还是没能吃饱,由于Objext.wait()方法调用后,只能由notify或notifyAll方法进行唤醒,才能继续执行,所以他只吃了一个之后进入等餐状态就一直等下去了,直到取餐台满了,厨师去休息了,俩个人永远的互相等待了下去,程序也没能停止。由此得到修改意见,增加notify(或notifyAll)方法。
第三版(一步步修改,当前依然存在问题,不要复制):
public synchronized void push(Hamburger hamburger){
if (index == 10){
System.out.println(Thread.currentThread().getName() + "休息了");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify();
System.out.println(Thread.currentThread().getName() + "制作了汉堡" + hamburger);
foods[index] = hamburger;
index++;
}
//顾客从取餐台取走汉堡
public synchronized Hamburger pop(){
if (index == 0){
System.out.println(Thread.currentThread().getName() + "在等餐");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify();
index--;
System.out.println(Thread.currentThread().getName() + "吃掉了汉堡" + foods[index]);
return foods[index];
}
通过结果判断,貌似正确的完成了一个厨师一个顾客的场景,但一个餐厅应该有多个厨师和多个顾客,所以改变了主函数,启动了3个厨师线程,6个顾客线程。
第四版(一步步修改,当前依然存在问题,不要复制):
public static void main(String[] args) {
//餐厅中只有一个取餐台
Cabinet cabinet = new Cabinet();
Cooker cooker = new Cooker(cabinet);
Customer customer = new Customer(cabinet);
Thread cookerThread = new Thread(cooker);
cookerThread.setName("0号厨师");
Thread cookerThread1 = new Thread(cooker);
cookerThread1.setName("1号厨师");
Thread cookerThread2 = new Thread(cooker);
cookerThread2.setName("2号厨师");
Thread customerThread = new Thread(customer);
customerThread.setName("零号顾客");
Thread customerThread1 = new Thread(customer);
customerThread1.setName("一号顾客");
Thread customerThread2 = new Thread(customer);
customerThread2.setName("二号顾客");
Thread customerThread3 = new Thread(customer);
customerThread3.setName("三号顾客");
Thread customerThread4 = new Thread(customer);
customerThread4.setName("四号顾客");
Thread customerThread5 = new Thread(customer);
customerThread5.setName("五号顾客");
Thread customerThread6 = new Thread(customer);
customerThread6.setName("六号顾客");
cookerThread.start();
cookerThread1.start();
cookerThread2.start();
customerThread.start();
customerThread1.start();
customerThread2.start();
customerThread3.start();
customerThread4.start();
customerThread5.start();
customerThread6.start();
}
报出了数组越界错误,出错原因发生在notify的过程中,本意是希望厨师做好了汉堡通知顾客吃,顾客吃完了汉堡通知厨师做,但是这边却成为拿到汉堡的顾客通知了另一个顾客去拿汉堡,碰巧通知者拿走的是最后一个汉堡。改进的地方在让顾客不要轻信其他顾客的话,自己看看取物台中是不是真的有剩余再吃。所以对if判断进行改进,改为while,得到第五版。
第五版(没有代码和逻辑问题了,但是还是不尽满意):
//厨师做好汉堡放入取餐台
public synchronized void push(Hamburger hamburger){
while (index == 10){
System.out.println(Thread.currentThread().getName() + "休息了");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify();
System.out.println(Thread.currentThread().getName() + "制作了汉堡" + hamburger);
foods[index] = hamburger;
index++;
}
//顾客从取餐台取走汉堡
public synchronized Hamburger pop(){
while (index == 0){
System.out.println(Thread.currentThread().getName() + "在等餐");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify();
index--;
System.out.println(Thread.currentThread().getName() + "吃掉了汉堡" + foods[index]);
return foods[index];
}
(运行结果太多了,省略截图,结局是顾客都吃饱走了,取餐台放满后,厨师都休息了。)
下一篇赋继续向真实情况改进。。。。。🐦