五 异常与多线程——第四节 等待唤醒机制

2022-06-05  本文已影响0人  杜艳_66c4

1、线程状态概述

6种状态

2、等待唤醒案例分析

Timed waiting 计时等待

Timed waiting

blocked 阻塞

blocked

waiting

通信

3、等待唤醒案例代码实现

package exception2.waitandnotify;

/**
 * created by apple on 2020/6/27
 * 等待唤醒案例:线程之间的通信
 * 创建一个顾客线程,消费者,告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入waiting状态,无限等待
 * 创建一个老板线程,生产者,花了5s做包子,做好包子之后,调用notify方法,唤醒顾客,吃包子。
 
注意事项:
 1、顾客和老板线程,使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行
 2、同步使用的锁对象,保证唯一
 3、只有锁对象,才能调用wait和notify方法,

 *Object类中的方法:
 *  void wait(),在其他线程调用此对象的notify()或notifyAll()方法前,导致当前线程等待
 *  void notify(),唤醒在此对象监视器上等待的单个线程,会继续执行wait()方法之后的代码
 *
 */
public class demo01waitandnotify {
    public static void main(String[] args) {
        //创建锁对象,保证唯一
        Object obj = new Object();
        //创建一个顾客线程
        //匿名内部类
        new Thread(){
           //重写run方法,
            @Override
            public void run() {
               //保证等待和唤醒的线程只有一个在执行,需要使用同步机制
                synchronized (obj){
                    System.out.println("告知老板要的包子的种类和数量");
                    //调用wait方法,放弃cpu的执行,进入waiting状态,无限等待
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //唤醒之后执行的代码
                    System.out.println("包子已经做好,开吃");
                }
            }
        }.start();
        //创建老板线程,
        new Thread(){
            @Override
            public void run() {
                //花了5s做包子
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //保证等待和唤醒的线程只有一个在执行,需要使用同步机制
                synchronized (obj){
                    System.out.println("老板5s之后做好包子,告知顾客,可以吃包子");
                    //,调用notify方法,唤醒顾客,吃包子。
                    obj.notify();
                }
            }
        }.start();
    }
}

4、Object类中wait带参方法和notify方法

package exception2.waitandnotify;

/**
 * created by apple on 2020/6/27
 
 */
public class Demo02waitAndNotify {
    public static void main(String[] args) {
        //创建锁对象,保证唯一
        Object obj = new Object();
        //创建一个顾客线程
        //匿名内部类
        new Thread(){
            //重写run方法,
            @Override
            public void run() {
                //保证等待和唤醒的线程只有一个在执行,需要使用同步机制
                synchronized (obj){
                    System.out.println("顾客一告知老板要的包子的种类和数量");
                    //调用wait方法,放弃cpu的执行,进入waiting状态,无限等待
                    try {
                        obj.wait(5000); //没人叫醒,自己醒了。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //唤醒之后执行的代码
                    System.out.println("包子已经做好,顾客一开吃");
                }
            }
        }.start();
        //创建一个顾客线程
        //匿名内部类
        new Thread(){
            //重写run方法,
            @Override
            public void run() {
                //保证等待和唤醒的线程只有一个在执行,需要使用同步机制
                synchronized (obj){
                    System.out.println("顾客二告知老板要的包子的种类和数量");
                    //调用wait方法,放弃cpu的执行,进入waiting状态,无限等待
                    try {
                        obj.wait(5000); //没人叫醒,自己醒了。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //唤醒之后执行的代码
                    System.out.println("包子已经做好,顾客二开吃");
                }
            }
        }.start();
        //创建老板线程,
        new Thread(){
            @Override
            public void run() {
                //花了5s做包子
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //保证等待和唤醒的线程只有一个在执行,需要使用同步机制
                synchronized (obj){
                    System.out.println("老板5s之后做好包子,告知顾客,可以吃包子");
                   // obj.notify();  如果有多个等待线程,随机唤醒一个
                    //,调用notify方法,唤醒顾客,吃包子。唤醒所有等待线程,
                    obj.notifyAll();  //
                }
            }
        }.start();
    }
}

5、线程间通信

5.1 概念:多个线程在处理同一个资源,但处理的动作(线程的任务)不同。
比如:生产包子。吃包子
5.2 为什么要处理线程间通信
多个线程并发执行时 ,默认情况CPU 随机切换线程,当需要多个线程共同完成一件任务, 并且希望有规律的执行,那多线程之间需要一些通信,以达到多线程共同操作一份数据
方式: 等待唤醒机制

6、 等待唤醒机制概述

多个线程间协作机制


等待唤醒中的方法

重点:有效利用资源

7、 等待唤醒机制需求分析

等待唤醒机制分析需要的类

8、 等待唤醒机制代码实现——包子类& 包子铺

根据上面的图,创建包子类

package con.day13.demo05.Thread.ThreadWait;

public class BaoZi {
    /*
    资源类:包子类
    属性 皮,馅, 状态
     */
    String pi;
    String xian;
    boolean flag = false;
}

包子铺

package con.day13.demo05.Thread.ThreadWait;

import jdk.nashorn.internal.ir.CallNode;

public class BaoZiPu extends Thread{
    /*
    生成者。
    注意:
    1、包子铺和包子的关系通信关系,互斥关系,必须使用同步技术,保证两个线程只有一个在执行
    2、锁对象保证唯一, 可以使用包子对象作为锁对象
    3、包子铺类和吃货类就需要把包子对象作为参数传递过来,
        1、需要在成员位置创建一个包子变量
        2、使用带参数构造方法,为包子变量赋值
     */
    //1、需要在成员位置创建一个包子变量
    private BaoZi bz;
   //2、使用带参数构造方法,为包子变量赋值
    public BaoZiPu (BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        //定义一个变量
        int count = 0;
        //让包子铺一直生产包子
        while (true){
            //必须同步技术保证两个线程只能有一个在执行
            synchronized (bz){
                //对包子的状态进行判断
                if (bz.flag == true){
                    //有包子,包子铺调用wait方法进入等待状态
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //没有包子,包子铺生成包子,增加趣味性,交替生成两种包子
                if (count %2 == 0){
                    //生产薄皮 三鲜包子
                    bz.pi = "薄皮";
                    bz.xian = "三鲜";
                }else{
                    //生产厚皮,牛肉馅包子
                    bz.pi = "厚皮";
                    bz.xian = "牛肉馅";
                }
                count++;
                System.out.println("包子铺正在生产" + bz.pi+ bz.xian + "的包子");
                //生产需要3s
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //生产完包子。修改包子状态为true
                bz.flag = true;
                //唤醒吃货线程
                bz.notify();
                System.out.println("包子铺已经生产好了" + bz.pi + bz.xian + "可以开始吃了");
            }
        }


    }
}

9、 等待唤醒机制代码实现——吃货类&测试

根据上面的图,创建吃货类,

package con.day13.demo05.Thread.ThreadWait;

public class Chihuo extends Thread{
    //1、需要在成员位置创建一个包子变量
    private BaoZi bz;
    //2、使用带参数构造方法,为包子变量赋值
    public Chihuo (BaoZi bz) {
        this.bz = bz;
    }
 //设置线程任务,吃包子
    @Override
    public void run() {
        //使用死循环,让吃货一直吃包子
        while (true){
            synchronized (bz){
                //对包子状态进行判断
                if (bz.flag = false){
                    //没包子,吃货调用wait方法进入等待状态
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    //被唤醒后,执行代码,吃包子
                    System.out.println("吃货吃" + bz.pi + bz.xian + "包子");
                    //吃货吃完,修改状态
                    bz.flag = false;
                    //吃货唤醒包子铺线程,
                    bz.notify();
                    System.out.println("吃货已经把" + bz.pi + bz.xian + "包子吃完了");
                }

            }
        }
    }
}

测试类:

package con.day13.demo05.Thread.ThreadWait;

public class DemoC {
    public static void main(String[] args) {
        //创建包子对象
        BaoZi bz = new BaoZi();
        //创建包子铺线程,开始生产包子
        new BaoZiPu(bz).start();
        //创建吃货线程,开始吃包子
        new Chihuo(bz).start();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读