等待唤醒机制

等待唤醒机制

1.1 线程间通信

概念: 多个线程在处理同一个资源,但是处理的动作(线程任务)却不同,那么多线程之间就存在通信问题。

比如:线程A用来生产包子的,线程B用来吃包子的,包子可以理解为同一个资源,线程A与线程B处理动作,一个是生产,一个是消费,那么线程A和线程B之间就存在线程通信问题。

为什么要处理线程间通信:

多个线程并发执行,在默认情况下线程的调度是抢占式的,当我们需要多个线程来完成一个任务,并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信,以此来达到多线程共同操作同一份数据。

如何保证线程间通信有效利用资源:

多个线程在处理同一个资源,并且任务不同时,需要线程间通信来帮助解决线程间对同一个变量的使用或者操作。就是多个线程在操作同一份数据时,避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程间能有效的利用资源.而这种手段即——等待唤醒机制

1.2 等待唤醒机制

什么是等待唤醒机制:

这是多线程间的一种协作机制。就是在一个线程进行了规定操作后,就进入等待状态(wait()),等待其他线程执行完它们指定代码后再将其唤醒(notify());在有多个线程进行等待时,如果需要,可以使用notifyAll()来唤醒所有等待线程.

wait/notify 就是线程间的一种协作机制.

等待唤醒中的方法

等待唤醒机制就是用于解决线程间通信问题的,使用到的3个方法的含义如下:

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

调用wait和notify方法需要注意的细节

  1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
  2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
  3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
1.3 两个线程间的有效通信案例
  • 生产者与消费者问题

就包子铺和消费者为例:

包子铺线程生产包子,吃货线程消费包子。
当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子(即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。
接下来,吃货线程能否进一步执行则取决于锁的获取情况。
如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。
包子铺线程能否进一步执行则取决于锁的获取情况。

代码演示:

包子类资源:

public class BaoZi {
     String  pier ;
     String  xianer ;
     boolean  flag = false ;//包子资源 是否存在  包子资源状态
}

吃货线程类:

public class ChiHuo extends Thread{
    private BaoZi bz;

    public ChiHuo(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }
    @Override
    public void run() {
        while(true){
            synchronized (bz){
                if(bz.flag == false){//没包子
                    try {
                        bz.wait(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");
                bz.flag = false;
                bz.notify();
            }
        }
    }
}

包子铺线程类:

public class BaoZiPu extends Thread {

    private BaoZi bz;

    public BaoZiPu(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }

    @Override
    public void run() {
        int count = 0;
        //造包子
        while(true){
            //同步
            synchronized (bz){
                if(bz.flag == true){//包子资源  存在
                    try {

                        bz.wait(500);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 没有包子  造包子
                System.out.println("包子铺开始做包子");
                if(count%2 == 0){
                    // 面皮  三鲜
                    bz.pier = "面皮";
                    bz.xianer = "三鲜";
                }else{
                    // 薄皮  牛肉
                    bz.pier = "薄皮";
                    bz.xianer = "牛肉";
                }
                count++;

                bz.flag=true;
                System.out.println("包子造好了:"+bz.pier+bz.xianer);
                System.out.println("吃货来吃吧");
                //唤醒等待线程 (吃货)
                bz.notify();
            }
        }
    }
}

测试类:

public class Demo {
    public static void main(String[] args) {
        //等待唤醒案例
        BaoZi bz = new BaoZi();

        ChiHuo ch = new ChiHuo("吃货",bz);
        BaoZiPu bzp = new BaoZiPu("包子铺",bz);

        ch.start();
        bzp.start();
    }
}

执行效果:

包子铺开始做包子
包子造好了:面皮三鲜
吃货来吃吧
吃货正在吃面皮三鲜包子
包子铺开始做包子
包子造好了:薄皮牛肉
吃货来吃吧
吃货正在吃薄皮牛肉包子
包子铺开始做包子
包子造好了:面皮三鲜
吃货来吃吧
吃货正在吃面皮三鲜包子
1.4三个线程间的有效通信案例
多个线程想要实现同步,多个线程必须加同一把锁 --->实现线程安全
    实现有效通信: 让A,B,C线程交替执行
         如果A线程在执行,那么BC线程就只能进入无线等待,然后A执行完了,就得唤醒所有等待线程
         如果B线程在执行,那么AC线程就只能进入无线等待,然后B执行完了,就得唤醒所有等待线程
         如果C线程在执行,那么AB线程就只能进入无线等待,然后C执行完了,就得唤醒所有等待线程

         如果A线程执行完毕,那么必须让B线程抢到,即使AC抢到也得进入无限等待
         B执行完之后,唤醒所有线程,必须让C线程抢到,即使AB抢到了也得进入无限等待
         C执行完之后,唤醒所有线程,必须让A线程抢到,即使BC抢到了也得进入无限等待

         A线程执行完毕,那么必须让B线程抢到,即使AC抢到也得进入无限等待
         .....

砖类:

public class Zhuan {
    int num ;
}

砖厂:

public class ZhuanCang implements Runnable{
    Zhuan z;

    public ZhuanCang(Zhuan z) {
        this.z = z;
    }

    @Override
    public void run() {

        while (true) {
            synchronized (z) {
               while (z.num % 3 != 0) {//所以改用while语句,当被唤醒后如果抢到锁还是会进行判断
        //if (z.num % 3 != 1){如果用if结构,当线程被唤醒是直接往后执行,不能达到预期效果
                    try {
                        z.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
        //               }
                }
                System.out.println("造砖中");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("砖造好了,搬砖工快来搬");
                z.num++;
                z.notifyAll();
            }
        }
    }
}

搬砖工:

public class BanZhuanGong implements Runnable{
    Zhuan z;

    public BanZhuanGong(Zhuan z) {
        this.z = z;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (z){
               while (z.num % 3 != 1) {//所以改用while语句,当被唤醒后如果抢到锁还是会进行判断
//if (z.num % 3 != 1){如果用if结构,当线程被唤醒是直接往后执行,不能达到预期效果
                   try {
                       z.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
//               }
                }

                    z.notifyAll();
                    System.out.println("搬砖,搬砖,搬砖");
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("砖搬完了,休息");
                    z.num++;
            }
        }

    }
}

建筑师:

public class JianZuShi implements Runnable {
    Zhuan z;

    public JianZuShi(Zhuan z) {
        this.z = z;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (z) {
                while (z.num % 3 != 2){//所以改用while语句,当被唤醒后如果抢到锁还是会进行判断
//if (z.num % 3 != 1){如果用if结构,当线程被唤醒是直接往后执行,不能达到预期效果
                    try {
                        z.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //               }
                }
                    System.out.println("建房子中");
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("没砖了,休息,等搬砖工去搬砖");
                    z.num++;
                    z.notifyAll();
            }
        }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Zhuan z = new Zhuan();

        ZhuanCang zc = new ZhuanCang(z);
        BanZhuanGong bzg = new BanZhuanGong(z);
        JianZuShi jzs = new JianZuShi(z);

        Thread t1 = new Thread(zc);
        Thread t2 = new Thread(bzg);
        Thread t3 = new Thread(jzs);

        t1.start();

        t2.start();

        t3.start();
    }

}

运行结果:

造砖中
砖造好了,搬砖工快来搬
搬砖,搬砖,搬砖
砖搬完了,休息
建房子中
没砖了,休息,等搬砖工去搬砖
造砖中
砖造好了,搬砖工快来搬
搬砖,搬砖,搬砖
砖搬完了,休息
建房子中
没砖了,休息,等搬砖工去搬砖

总结:

1.调用wait()和notfiy()以及notifyAll()方法的锁对象要一致
2.线程的调度是抢占式调度
3.当线程进入了无限等待,那么该线程就不会抢占锁对象,和cpu资源
4.线程如果从无限等待状态被唤醒了,那么当抢到锁对象,就会继续从进入无限等待的位置继续往下执行

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值