概念
是一个典型的多线程通信问题,当生产者生产时,消费者进行休眠;当生产者结束生产,唤醒消费者进行消费,生产者休眠。
在线程调用的类中需要使用同步方法关键字synchronized
,这是为了防止两个线程同时使用方法,调用同一个对象中的属性。
实例代码
代码中主要有两个线程,一个叫Cook,一个叫Waiter,他们各自可以调用名为Food的类对象中的一个方法。
Cook和Waiter实现线程的方法是继承Thread类,重写run方法
Cook是厨师,英语中职业类名词唯一不加-er或-or后缀的那个。
问题代码
Cook打算做100份菜,分别是50份玉米糁子和50份宫保鸡丁;Waiter负责在Cook做好以后端走这饭菜。
因为盘子只有一个,所以Cook在Waiter把盘子端回来之前不能做下一份菜,那Cook要怎么办呢?
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
public static class Cook extends Thread{
Food f;
Cook(Food f){
this.f = f;
}
@Override
public void run() {
for(int i =0; i<100; i++){
if(i%2 ==0){
try {
f.setNameAndTaste("玉米糁子","甜");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
f.setNameAndTaste("宫保鸡丁","香辣");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
static class Waiter extends Thread{
Food f;
Waiter(Food f){
this.f = f;
}
@Override
public void run(){
for(int i =0; i<100; i++){
try {
f.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
f.get(f);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static class Food extends Thread{
private String name;
private String taste;
private boolean flag = true;
public void setNameAndTaste(String name, String taste) throws InterruptedException {
if(flag){
this.name = name;
sleep(100);
this.taste = taste;
flag =false;
this.notifyAll();
this.wait();
}
}
public void get(Food f) throws InterruptedException {
if(!flag){
System.out.println("服务员端走了"+f.name+",味道是:"+f.taste);
flag = true;
this.notifyAll();
this.wait();
}
}
}
}
第一次的代码不成功,报错如下
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.base/java.lang.Object.notifyAll(Native Method)
at ThreadDemo.ThreadDemo$Food.setNameAndTaste(ThreadDemo.java:79)
at ThreadDemo.ThreadDemo$Cook.run(ThreadDemo.java:25)
服务员端走了玉米糁子,味道是:甜
Exception in thread "Thread-2" java.lang.IllegalMonitorStateException
at java.base/java.lang.Object.notifyAll(Native Method)
at ThreadDemo.ThreadDemo$Food.get(ThreadDemo.java:89)
at ThreadDemo.ThreadDemo$Waiter.run(ThreadDemo.java:55)
更改代码
更改后的代码如下,注意看注释
//食物
static class Food{
private String name;
private String taste;
//true表示可以生产
private boolean flag = true;
//setNameAndTaste这个方法需要加上关键字synchronized
public synchronized void setNameAndTaste(String name, String taste){
if(flag){
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag =false;
this.notifyAll();
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
//get也要加上关键字synchronized
public synchronized void get() {
if(!flag){
System.out.println("服务员端走了"+name+",味道是:"+taste);
flag = true;
this.notifyAll();
try {
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
需要加关键字的原因在于:
厨师和服务员两个线程调用Food,如果不限制同步方法,厨师会在没有盘子的情况下开始做饭,并导致出错(虽然我也不知道内部究竟怎么回事,为啥只有一个盘子。。)