java多线程之--(4)线程间通信+等待唤醒机制详解

概念: 很多的时候一件事情需要很多人共同完成,这时就免不了相互之间的交流!! 同理利用多线程进行工作时,为了 能够使得线程之间能够更好的协调完成任务,这时就需要线程之间的交流,我们称之为线程间通信!

1. 线程间通信

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

在这里插入图片描述
为什么要处理线程间通信:

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

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

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

2. 等待唤醒机制

2.1 什么是等待唤醒机制:

谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁!!!,但是竞争关系这并不是多线程的全部,线程间也会有协作机制。就好比在公司里你和你的同事们,你们可能存在在晋升时的竞争,但更多时候你们更多是一起合作以完成某些任务

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


2.2 等待唤醒中的方法

  • wait: 线程持有锁对象,然后调用锁对象.wait()方法,进入等待状态(此时线程会被加入到wait set中)同时释放它对锁的拥有权, 然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行
  • notify: 另外一个线程通过锁对象.notify方法,随机唤醒在此锁对象上等待的某一条线程对象;例如,餐馆有空位置后,等候就餐最久的顾客最先入座
  • notifyAll: 另外一个线程通知锁对象上等待的全部线程

注意:

哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而 此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调 用 wait 方法之后的地方恢复执行

总结:

  • 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态
  • 否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态

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

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

3.生产者与消费者问题

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

举例说明生产者与消费者问题:

  • 创建包子对象, BaoZi
/**
 * @ Description
 * @ auther          宁宁小可爱
 * @ create          2020-1-10 21:14
 **/

public class BaoZi {
    //boolean变量,表示包子是否存在
    boolean flag; //默认值false
    //皮儿
    String pier; // null
    //馅儿
    String xianer; // null
}

  • 创建生产者与消费者 ProducterAndConsumer
/**
 * @ Description
 * @ auther          宁宁小可爱
 * @ create          2020-1-10 21:15
 **/

public class ProducterAndConsumer {
    public static void main(String[] args) {
        //1.线程1,生产者线程,功能是生成一个包子
        //2.线程2,消费者线程,功能是吃掉线程1生成出来的那个包子
        //要求: 线程1 和 线程2 不断的反复生成和吃包子
        //3.定义一个包子对象
        BaoZi bz = new BaoZi();
        Object obj = new Object();

        //4.第一个线程,生产者线程
        Thread producter = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    synchronized (obj) {
                        //1.必须判断包子是否存在
                        if (bz.flag) {
                            //有包子,生产者必须等待
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //2.制作包子
                        bz.flag = true;
                        bz.pier = "水晶皮";
                        bz.xianer = "玉米蔬菜猪肉";
                        //3.唤醒消费者来吃
                        System.out.println("包子做好了(" + bz.pier + bz.xianer + "),快来吃吧...");
                        obj.notify();

                    }
                }
            }
        });

        //5.第二个线程,消费者线程
        Thread consumer = new Thread(new Runnable() {
            @Override
            public void run(){
                while(true) {
                    synchronized (obj) {
                        //1.必须先判断包子是否已经存在
                        if (!bz.flag) {
                            //没有包子,消费者必须等待
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //2.线程2发现有包子吃包子
                        bz.flag = false;
                        System.out.println("包子(" + bz.pier + bz.xianer + ")吃完了,赶紧来做.....");
                        bz.pier = null;
                        bz.xianer = null;
                        //3.唤醒生产者线程制作包子
                        obj.notify();
                    }
                }
            }
        });
        //同时启动
        producter.start();
        consumer.start();

    }
}

  • 执行结果

在这里插入图片描述

这就是最经典的生产者与消费者关系,这里就用到了线程间的通信

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我三胖哥哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值