Java多线程四:线程间通信/等待唤醒机制

时间:2021-11-09 17:29:33

多线程等待唤醒机制

介绍

线程间通信方式
1、全局变量(基于内存共享)
2、Message消息机制
备注:基于内存共享比较容易实现

如果多线程只是处理完全相同的任务时,那么事情就简单了,似乎也不需要线程之间相互协同。

如果多线程处理的业务需要相互协同的话,那么线程之间就要进行协同和通信了。
最典型的例子就是生产者和消费者模型。

Java在Object对象中就定义了几个关于等待唤醒机制的方法
1、public final void wait()
//使当前线程处于等待状态,直到其它线程调用notify或notifyAll方法唤醒
//简单说就是释放锁资源执行权,等其它线程通知唤醒它。
2、public final native void notify()
//唤醒在此对象监视器上等待的单个线程
3、public final native void notifyAll()
//唤醒在此对象监视器上等待的所有线程

备注:以上三个方法都是只有在持有对象监视器时才可以调用,否则报错。通常是在同步代码块中使用。

它们的作用对象也只能是监视本对象的线程,不能作用于其它无关线程。

wait():等待,将正在执行的线程释放其执行资格和执行权,并存储到线程池中。
notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
notifyAll():唤醒全部:可以将线程池中的所有wait() 线程都唤醒。


这三个方法被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?

因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。

等待和唤醒机制详解

举个例子

如果有两个人A、B,他们要完成清洁盘子工作,如果他们都一起洗,洗完之后一起擦干那么他们就属于多线程处理相同的任务,
他们之间并没有协同和信息交流。
如果A负责洗盘子,B负责擦盘子,那么他们之间就存在协同和信息传递了。当A洗完一个盘子之后,它会通知B,
此时A接到通知之后又开始洗盘子了,如此循环下去直到洗完为止。
备注:这就是一个生产者和一个消费者时候的模型

如果有A、B、C、D四个人,A、B负责洗盘子,C、D复杂擦干盘子,刚开始A洗一个盘子,通知C、D,此时C、D决定C先擦盘子D先休息,
C擦完之后通知A、B,这时B开始洗盘子A休息,B洗完之后通知C、D,此时D擦盘子C休息,依次循环下去知道任务完成。
备注:这就是多个生产者和多个消费者模型

以上的三种模型都是针对每次只能洗一个盘子和只能擦干一个盘子的情况(比如临时中间盘架只有一个放盘子的位置)


实际开发和场景中,往往都是生产者一直生产,将生产好的产品放到一个队列容器中,消费者就只观察队列,如果队列中有产品就消费。
这样就是生产者和消费者之间免去了不必要的信息交流,也就是解耦合了。
备注:实际开发中使用消息中间件场景比较多。

总结

多线程如果是存在协同处理任务的话,处理不好容易出现死锁。
所以在处理这种问题时要分析清楚何时等待何时唤醒,即线程间通信要清晰明了。