等待唤醒机制概述和等待唤醒机制需求分析和实现
什么是等待唤醒机制
这是多个线程间的一种协作机制。谈到线程我们经常想到了是线程间的竞争(race),比如去争夺锁,但是这并不是故事的全部,线程间也会有写作机制。就好比在公司里你和你们的同事门,你们可能存在晋升时的竞争,但更多时候你们呢更多是一起合作完成某些任务
就是在一个线程进行了规定操作后,就进入等待状态(wait),等待其他线程执行完他们指定的代码过后再将其唤醒(notity)在有多个线程进行等待的时候,如果需要可以使用notifyAll来唤醒所有等待的线程
wait/notify就是线程间的一种协作机制
等待唤醒中的方法:
等待唤醒即使就是用于解决线程间通信问题,使用到的3个方法的含义如下:
1.wait:线程不在活动,不再参与调度,进入wait set中,因此不会浪费cpu资源,也不会去竞争锁了,这时的线程状态即使WAIYING。它还要等着别的线程执行一个特别的动作,也就是通知(notify)在这个对象上等待的线程从wait set中释放出来,重新进入调度队列中
2.notify:则选取所统治对象的wait set中的一个线程释放;例如,参观有空位之后,等候就餐醉酒的顾客最先入座
3、notifyAll:则释放所通知对象的wait set上的全部线程
注意
哪怕只通知了一个等待的线程,被通知线程也不能立即回复执行,因此它当初中断的地方是在同步快内,而此刻它已经不持有锁,所以他需要再次尝试去获取锁成功后才能在当初调用wait方法之后的地方恢复执行
调用wait和notify方法需要注意的细节
1.wait方法与notify方法必须要由一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程
2.wait方法与notify方法是属于Object类方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的
3.wait方法与notify方法必须要在同步代码块或者同步函数中使用,因为必须要通过锁对象调用这两个方法
等待唤醒机制需求分析
生产者与消费者问题
等待唤醒机制其实就是经典的生产者和消费者的问题
就拿生产包子消费包子来说的呢个带唤醒机制如何有效利用资源
包子铺线程生产包子,吃货线程消费包子。当包子没有的时候包子的状态为false,吃货线程等待,包子铺线程生产包子
即包子状态为(true)并通知吃货线程解除吃货的等待状态,因为已经有包子了,那么包子铺线程进入等待状态。接下来
吃货线程能否进一步取决与锁的获取情况。如果吃货获取到锁,那么就执行吃包子的动作,包子吃饭包子的状态为false
并通知包子铺线程,词汇或线程进入等待状态,包子铺线程能否进一步执行则取决与锁的获取情况。
分析:需要哪些类
资源类:包子类
设置包子属性:皮,陷,包子的状态:有 true,没有false
生产者(包子铺)类:是一个线程类,可以继承Thread
设置线程任务(run):生产包子
对包子的状态进行判断
true:有包子 包子铺调用wait方法进入等待状态
false:没包子 包子铺生产包子 增加一些趣味性:交替生产两种包子
两种状态(i%2==0)
包子铺生产好了包子修改包子的状态为true
唤醒吃货线程,让吃货线程吃包子
消费者(吃货)类:是一个线程类可以继承Thread
设置线程任务(run):吃包子
对包子的状态进行判断:
true 有:吃货吃包子,吃完修改包子的状态
false 没有:吃货线程调用wait进入等待状态
唤醒包子铺线程生产包子
测试类(Test):包含main方法是程序启动的入口
创建包子对象:创建包子铺线程开启:创建吃货线程开启
代码实现:
package com.yang.Test.ThreadStudy;
import lombok.SneakyThrows;
public class MyTest {
public static void main(String[] args) {
BaoZi baoZi = new BaoZi();
new BaoZiPu(baoZi).start();
new ChiHuo(baoZi).start();
}
}
class BaoZi {
String pi;
String xian;
boolean flag = false;
}
class ChiHuo extends Thread {
private BaoZi bz;
public ChiHuo(BaoZi bz) {
this.bz = bz;
}
@SneakyThrows
@Override
public void run() {
while(true){
synchronized (bz){
if (bz.flag == false) {
bz.wait();
}
System.out.println("吃货吃"+bz.pi+bz.xian);
bz.flag = false;
bz.notify();
System.out.println("吃货已经把"+bz.pi+bz.xian+"的包子吃饭了");
}
}
}
}
class BaoZiPu extends Thread {
private BaoZi bz;
public BaoZiPu(BaoZi bz) {
this.bz = bz;
}
@SneakyThrows
@Override
public void run() {
int count = 0;
while (true) {
synchronized (bz) {
if (bz.flag == true) {
bz.wait();
}
if (count % 2 == 0) {
bz.pi = "薄皮";
bz.xian = "三鲜馅";
} else {
bz.pi = "冰皮";
bz.xian = "牛肉大葱馅";
}
count++;
System.out.println("包子铺正在生产" + bz.pi + bz.xian + "包子");
Thread.sleep(3000);
bz.flag = true;
bz.notify();
System.out.println("包子铺已经生产好了" + bz.pi + bz.xian + "包子,吃货可以开始吃了");
}
}
}
}
运行结果: