1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Java 线程间的通信机制(等待和唤醒机制)

Java 线程间的通信机制(等待和唤醒机制)

时间:2022-11-09 03:29:39

相关推荐

Java 线程间的通信机制(等待和唤醒机制)

一、 线程间通信

概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。

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

1.1线程之间为什么需要进行通信?

我们都知道,要想能够去执行一个线程,首先这个线程需要获取CPU的执行权,当这个线程执行完毕之后,就会释放CPU资源,并发执行的时候,剩下的处于就绪状态的线程就会一起去争夺CPU的执行权,谁抢到谁就执行。但是在开发过程中,我们可能需要多个线程进行协调配合来完成一件事。并且需要线程之间有规律的去执行任务。这就需要线程之间相互通信了。也就是说当多个线程共同去争夺同一个cpu资源的时候,只能有一个线程能够抢到,其他没抢到的就会进入阻塞状态,等待抢到cpu资源的线程执行结束再去抢夺。例如:烤鸭店老板A将烤鸭生产好之后,就叫顾客B过来吃。B吃完之后,通知A继续生产制作。

注意:

A先生产,此时B在等待A生产好。

A生产好之后,通知B来吃,相当于B被A唤醒。

线程之间的通信依靠的是wait()notify()、notifyAll()方法进行协调。这三个方法都是定义在了Object类中。

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

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

1.3 什么是等待唤醒机制

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

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

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

二、为什么要定义在Object类中?

一提到定义在Object中就会想到同步锁,之前的一篇文章:线程安全—实现安全卖票中,就介绍了Java中每个对象有且仅有一个对象锁。也就是说所有的java对象都可以是同步对象,既然所有对象都能是同步对象,而且有wait()、notify()、notifyAll()方法,当然应该将这三个方法定义到Object超类中。

2.1 等待与唤醒方法基本概念:

wait():

当线程A调用wait()方法后,释放同步锁,进入阻塞状态,然后加入到等待锁对象的队列中。

notify():

线程B获取到同步锁之后,调用notify()方法,从等待锁的队列中唤醒一个线程(被唤醒的线程状态由等待转变成就绪,等线程B执行完毕释放了锁资源之后,被唤醒的线程获取到锁之后就会去执行该线程)

notifyAll():

线程B获取到同步锁之后,调用notifyAll()方法,会唤醒等待锁队列中所有的线程,等待线程B执行完之后释放锁资源,被唤醒的线程去争夺锁资源,获取到锁对象的线程会去执行相应的逻辑。

2.2 notify()和notifyAll()的区别总结:

notify()方法 仅仅会去通知等待队列中的其中一个线程,并且我们并不知道哪个线程会被唤醒,但是notifyAll()方法会唤醒等待队列中的所有处于等待状态的线程(如果此时我们的等待队列中只有一个处于等待状态的线程,那么两种唤醒方法的效果一样,但是如果等待队列中有两个或两个以上的等待状态线程,那么就需要主要两种唤醒方法的区别了)

三、sleep()方法和wait()方法有何区别?

可以结合:线程的生命周期来看。

sleep()他是定义在Thread类中的一个方法,当调用此方法的时候,线程可以由RUNNING状态转换为TIMED_WAITING状态,线程执行此方法的时候,将会释放CPU执行权,但是该线程依然会持有当前拥有的锁对象,(它释放了CPU的执行权之后,其他线程可以使用此CPU执行权去执行不依赖此对象锁的任务)。此方法用在什么位置没有特殊要求。

wait()他是定义在Object类中的一个方法,当调用此方法的时候,线程可以由RUNNING状态转换为WAITING状态,此状态也可以理解为“无线等待”状态,他需要配合notify()和notifyAll()方法来唤醒线程。另外,线程执行此方法的时候,将会释放CPU的执行权和持有的锁。此方法必须要用在synchronized同步代码块中。

 注意:以上方法需要用在同步代码块/方法中。调用wait()方法和notify()/notifyAll()方法的锁对象必须是同一个。

例题:

情景:包子铺生产者消费者案例:(包子铺老板A将包子生产好之后,叫吃货B过来吃,B吃完之后,通知A继续生产制作。)

分析:

代码:

实体类

public class BaoZi {String name;//包子的名称Boolean flag;//包子的状态(true表示存在 false 表示不存在)}

消费者类(吃货B)

public class ChiHuo extends Thread{BaoZi baoZi;//构造函数:用来指定线程的名称和操作资源public ChiHuo(String name,BaoZi baoZi){super(name);this.baoZi=baoZi;}public void run(){String threadName=Thread.currentThread().getName();int count=0;while(true){synchronized (baoZi){count++;if (count>10){break;}if(baoZi.flag){//如果包子存在System.out.println(threadName+"开始吃"+baoZi.name);baoZi.flag=false;//修改状态baoZi.notify();//唤醒其他资源状态}else{//如果包子不存在try {baoZi.wait();//进入等待状态} catch (InterruptedException e) {e.printStackTrace();}}}}}}

生产者类(包子铺老板A)

public class ZaoCanDian extends Thread{BaoZi baoZi;//构造函数:用来指定线程的名称和操作资源public ZaoCanDian(String name, BaoZi baoZi) {super(name);this.baoZi = baoZi;}public void run() {String threadName = Thread.currentThread().getName();int count = 0;while (true) {synchronized (baoZi) {count++;if (count > 10) {break;}if (baoZi.flag) {//如果包子存在try {baoZi.wait();} catch (InterruptedException e) {e.printStackTrace();}} else {//如果包子不存在System.out.println(threadName + "开始制作" + baoZi.name);baoZi.flag=true;//更改包子状态baoZi.notify();//唤醒同一资源下的其他线程}}}}}

测试类

public class ThreadTest {public static void main(String[] args) {//定义资源对象BaoZi baoZi=new BaoZi();baoZi.name="豆沙包";baoZi.flag=true;//定义两个线程,起名字且操作同一对象ChiHuo ch=new ChiHuo("吃货",baoZi);ZaoCanDian zcd=new ZaoCanDian("圆滚滚包子铺",baoZi);//启动线程zcd.start();ch.start();}}

测试结果

吃货开始吃豆沙包圆滚滚包子铺开始制作豆沙包吃货开始吃豆沙包圆滚滚包子铺开始制作豆沙包吃货开始吃豆沙包圆滚滚包子铺开始制作豆沙包吃货开始吃豆沙包圆滚滚包子铺开始制作豆沙包吃货开始吃豆沙包圆滚滚包子铺开始制作豆沙包

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。