经典等待唤醒
需求分析:
有一个桌子,桌子上有一个蛋糕,当顾客线程先抢到CPU,则先执行顾客线程,上锁,首先看桌子,若有蛋糕顾客去吃,吃完了唤醒厨师线程去抢CPU做蛋糕,自己再继续抢CPU;若没蛋糕则等待,解锁,让厨师运行厨师线程做蛋糕。若厨师先抢到CPU看到桌子,若有蛋糕,厨师等待,解锁,把cpu让给顾客,执行顾客线程;若无蛋糕,厨师做蛋糕,做完蛋糕唤醒顾客线程去抢CPU,自己再继续抢CPU。顾客厨师反复抢CPU,直到达到run结束的条件。
若唤醒其它线程后,自己又抢到CPU,则继续唤醒,再抢,直到被唤醒的线程抢到CPU。
tips:
wait()使当前线程等待(不参与抢cpu),并且释放lock,让其它线程可以访问同步代码块。
两个线程共用同一把锁,执行线程时操作同一资源。
详细流程图如下:
图1 流程图
详细代码:
1.厨师线程
public class Cook extends Thread{
@Override
public void run() {
while (true){
// 加锁
synchronized (Desk.obj){
// 线程结束表示
if(Desk.count==0){
System.out.println("营业结束");
break;}
// 判断有无蛋糕
if(Desk.flag==0){
// 没有蛋糕,做一个
Desk.flag=1;
System.out.println("厨师做蛋糕");
// 唤醒顾客吃
Desk.obj.notifyAll();
}else {
// 有蛋糕,等待顾客eater唤醒
try {
Desk.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
2.顾客线程
public class Eater extends Thread {
@Override
public void run() {
while (true){
// 首先加锁
synchronized (Desk.obj){
// 剩余蛋糕数作为结束线程的判断
if(Desk.count==0){
System.out.println("营业结束");
break;}
// 判断桌子上有无吃的
if(Desk.flag==0){
// 没有,则等待厨师唤醒
System.out.println("顾客等蛋糕");
try {
Desk.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
// 有,则吃一个
Desk.flag=0;
// 修改桌子上的状态
System.out.println("顾客吃了一个,还剩"+--Desk.count+"个");
// 唤醒厨师做
Desk.obj.notifyAll();
}
}
}
}
}
3.桌子
public class Desk {
public static int count=10;//总共蛋糕数
public static int flag=0;//0 表示桌子上没有蛋糕,1表示桌子上有蛋糕
static Object obj=new Object();//锁对象
}
4.测试类
public class eatCake {
public static void main(String[] args) {
// 创建顾客和厨师线程对象
Eater eater=new Eater();
Cook cook=new Cook();
// 启动线程
cook.start();
eater.start();
}
}