Java SE多线程部分--21.等待唤醒机制

版权声明:转载请注明原始链接 https://blog.csdn.net/sswqzx/article/details/82904635

1、概述

等待唤醒机制就是用于解决线程间通信的问题、常见的方法如下:

1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,
这时的线程状态即是 WAITING。它还要执行一个特别的动作,也即是“通知(notify)”
在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,
等候就餐最久的顾客最先入座。
3. notifyAll:则释放所通知对象的 wait set 上的全部线程。

注意细节:
1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。

下面来说下生产者和消费者的问题 :

线程A用来生产了一产品,线程B用来消费了一个产品,产品可以理解为同一资源,线程A与线程B处理的动作,
一个是生产,一个是消费,那么线程A与线程B之间就要用到等待唤醒机制。

2、实例

例子 :以包子为例

生产线程已经生产完毕包子了, 此时, 不再生产, 而是等待消费线程来消费. 如果生产线程没有包子, 此时, 就需要生产, 生产完毕后, 唤醒消费线程来消费.

    消费线程消费完毕包子后, 不再消费, 此时, 消费线程就需要唤醒生产线程来生产. 如果消费线程没有包子, 消费线程就进入到等待状态.

 共享资源类 :

public class BaoZi {
    // 属性
    private String pier;
    private String xianer;

    // 类型
    boolean type = false;
    boolean flag = false;

    public BaoZi(String pier, String xianer) {
        this.pier = pier;
        this.xianer = xianer;
    }

    public BaoZi() {
    }

    @Override
    public String toString() {
        return "BaoZi{" +
                "pier='" + pier + '\'' +
                ", xianer='" + xianer + '\'' +
                '}';
    }

    public String getPier() {
        return pier;
    }

    public void setPier(String pier) {
        this.pier = pier;
    }

    public String getXianer() {
        return xianer;
    }

    public void setXianer(String xianer) {
        this.xianer = xianer;
    }
}

生产线程 :

public class BaoZiPu extends Thread {
    // 属性 (记录数据)
    private BaoZi baoZi;

    // 构造方法
    public BaoZiPu(BaoZi baoZi, String name) {
        super(name);
        this.baoZi = baoZi;
    }

    // 重写 Thread 类的 run 方法
    @Override
    public void run() {
        // 不断生产
        while (true) {

            // 同步锁环境
            synchronized (baoZi) {
                // 生产功能
                produce();
            }
        }
    }

    // 生产功能 : 子线程
    public void produce() {

        // 随机时间切换 : 0 ~ 500 毫秒   (long)(Math.random() * 500)
        /*try {
            Thread.sleep((long)(Math.random() * 500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        // 1. 有包子, 生产线程进入等待
        if (baoZi.flag == true) {
            try {
                baoZi.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 2. 没有包子, 生产线程进行生产
        // 2.1 判断数据类型, 赋值不同的数据
        if (baoZi.type == false) {
            // 大葱牛肉
            baoZi.setPier("大葱");
            baoZi.setXianer("牛肉");
        } else {
            // 金牌虾仁
            baoZi.setPier("金牌");
            baoZi.setXianer("虾仁");
        }

        // 2.2 更改类型 (类型取反即可)
        baoZi.type = !baoZi.type;

        // 2.3 输出语句
        System.out.println(Thread.currentThread().getName() + " 生产了" + baoZi.getPier() + baoZi.getXianer() + " 的包子.");

        // 2.4 更改包子的标记
        baoZi.flag = true;

        // 3. 唤醒消费线程
        baoZi.notify();
    }
}

消费线程 :

public class ChiHuo extends Thread  {
    // 属性 (记录数据)
    private BaoZi baoZi;

    // 构造方法
    public ChiHuo(BaoZi baoZi, String name) {
        super(name);
        this.baoZi = baoZi;
    }

    // 重写 Thread 类的 run 方法
    @Override
    public void run() {
        // 不断消费
        while (true) {
            synchronized (baoZi) {
                // 消费功能
                consume();
            }
        }
    }

    // 消费功能
    public void consume() {

        // 随机时间切换 : 0 ~ 500 毫秒   (long)(Math.random() * 500)
        /*try {
            Thread.sleep((long)(Math.random() * 500));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        // 1. 判断, 没有包子, 死等
        if (baoZi.flag == false) {
            try {
                baoZi.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 2.1 取出数据, 消费
        System.out.println(Thread.currentThread().getName() + " 正在吃 " + baoZi.getPier() + baoZi.getXianer() + " 的包子.");

        // 2.2 将数据清空
        baoZi.setPier("null");
        baoZi.setXianer("null");

        // 2.3 修改包子的标记
        baoZi.flag = false;

        // 3. 唤醒生产线程生产包子
        baoZi.notify();
    }
}

测试类 :

public class ProduceAndConsumeTest {
    public static void main(String[] args) {

        // 准备一个共享数据类
        BaoZi baoZi = new BaoZi();

        // 生产线程
        BaoZiPu baoZiPu = new BaoZiPu(baoZi, "生产线程");
        // 消费线程
        ChiHuo chiHuo = new ChiHuo(baoZi, "消费线程");

        // 启动线程
        baoZiPu.start();
        chiHuo.start();
    }
}

猜你喜欢

转载自blog.csdn.net/sswqzx/article/details/82904635