等待和唤醒 wait和notify
小案例(消费之和生产者包子关系)
/*
* 等待唤醒案例:线程之间的通信
* 创建一个顾客线程(消费者):告知老板要的包子种类和数量,调用wait方法,放弃CPU执行,进入到WAITING状态(无限等待)
* 创建一个老板线程(生产者):花了5秒做包子,做好了之后,调用notify方法,唤醒顾客吃包子
*
* 注意:
* 1.顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只有一个在执行
* 2.同步使用的锁对象那个必须保证唯一
* 3.只有锁对象才能调用wait和notify方法
*
* Obejct类中的方法
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void notify()
唤醒在此对象监视器上等待的单个线程。
会继续执行wait方法之后的代码
* */
public class Test10 {
public static void main(String[] args) {
// 创建一个锁对象obj
Object obj = new Object();
// 创建一个消费者线程
new Thread() {
@Override
public void run() {
// 无限循环,一直处于在买包子的状态
while(true){
synchronized (obj) {
System.out.println("消费者告诉老板买包子的种类和数量");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行
System.out.println("老板包子已经生产好了,开吃");
System.out.println("--------------------");
}
}
}
}.start();
//创建一个老板的线程
new Thread(){
@Override
public void run(){
//老板一直循环生产包子
while(true){
synchronized (obj) {
System.out.println("老板收到消费者的信息之后开始生产包子");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("花费了5秒后包子生产完成了");
//唤醒消费者,包子好了,可以开吃了
obj.notify();
}
}
}
}.start();
}
}
/*
* 进入到TImeWaiting(计时等待)有两种方式
* 1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
* 2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动唤醒,线程睡醒进入到Runnable/Blocked状态
*
* 唤醒的方法:
* void notify() 唤醒在此对象监视器上等待的单个线程。 如果有多个等待线程,随机唤醒一个
void notifyAll() 唤醒在此对象监视器上等待的所有线程。 唤醒所有等待的线程
*
* */
下面是一个案例,包子铺和吃货之间的通信
包子类
/*
资源类:包子类
设置包子的属性
皮
陷
包子的状态: 有 true,没有 false
*/
public class BaoZi {
String pi;
String xian;
boolean flag = false;
}
包子铺类
/*
生产者(包子铺)类:是一个线程类,可以继承Thread
设置线程任务(run):生产包子
对包子的状态进行判断
true:有包子
包子铺调用wait方法进入等待状态
false:没有包子
包子铺生产包子
增加一些趣味性:交替生产两种包子
有两种状态(i%2==0)
包子铺生产好了包子
修改包子的状态为true有
唤醒吃货线程,让吃货线程吃包子
注意:
包子铺线程和包子线程关系-->通信(互斥)
必须同时同步技术保证两个线程只能有一个在执行
锁对象必须保证唯一,可以使用包子对象作为锁对象
包子铺类和吃货的类就需要把包子对象作为参数传递进来
1.需要在成员位置创建一个包子变量
2.使用带参数构造方法,为这个包子变量赋值
*/
public class BaoZiPu extends Thread {
//需要在成员位置创建一个成员变量
BaoZi bao = new BaoZi();
//2.使用带参数构造方法,为这个包子变量赋值
public BaoZiPu(BaoZi bao) {
this.bao=bao;
}
@Override
public void run(){
//定义一个变量,进行判断生产什么样的包子
int count=0;
//一直生产包子
while(true){
//必须同时同步技术保证两个线程只能有一个在执行
synchronized (bao) {
//如果包子的状态为true,在生产者进入等待状态
if(bao.flag==true){
try {
bao.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产者,被唤醒之后,会进行生产包子
if(count%2==0){
bao.pi="薄皮";
bao.xian="三鲜馅";
}else{
bao.pi="冰皮";
bao.xian="猪肉大葱";
}
count++;
System.out.println("包子铺正在生产"+bao.pi+bao.xian+"的包子");
//生产包子需要3秒钟
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//包子生产好了,需要改变包子的状态为true
bao.flag=true;
//唤醒消费者,开始吃包子吧
bao.notify();
System.out.println("包子铺已经生产好了"+bao.pi+bao.xian+"包子,吃货可以开始吃了");
}
}
}
}
吃货类
/*
消费者(吃货)类:是一个线程类,可以继承Thread
设置线程任务(run):吃包子
对包子的状态进行判断
false:没有包子
吃货调用wait方法进入等待状态
true:有包子
吃货吃包子
吃货吃完包子
修改包子的状态为false没有
吃货唤醒包子铺线程,生产包子
*/
public class ChiHuo extends Thread {
//创建一个包子对象,作为参数传递
BaoZi bao = new BaoZi();
//创建一个有参数的构造器,为包子变量赋值
public ChiHuo(BaoZi bao) {
this.bao = bao;
}
@Override
public void run(){
//让消费者一直循环吃包子
while(true){
//创建有一个同步锁,保证线程安全
synchronized (bao) {
//判断包子的状态,如果为false进入等待状态
if(bao.flag==false){
try {
bao.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//唤醒以后,吃货就可以开始吃包子了
System.out.println("吃货正在吃"+bao.pi+bao.xian+"的包子");
//吃货吃完包子以后,改变包子的状态
bao.flag=false;
//唤醒包子铺开始生产包子
bao.notify();
System.out.println("吃货已经吃完了"+bao.pi+bao.xian+"的包子,包子铺开始生产包子");
System.out.println("=======================================");
}
}
}
}
测试类
public class Test {
public static void main(String[] args) {
BaoZi bao = new BaoZi();
new BaoZiPu(bao).start();
new ChiHuo(bao).start();
}
}