多线程系列(六) -等待和通知模型详解

一、简介

在之前的线程系列文章中,我们介绍了synchronizedvolatile关键字,使用它能解决线程同步的问题,但是它们无法解决线程之间协调和通信的问题。

举个简单的例子,比如线程 A 负责将 int 型变量 i 值累加操作到 10000,然后通知线程 B 负责把结果打印出来。

这个怎么实现呢?其中一个最简单的办法就是,线程 B 不断的通过轮询方式while(i == 10000)检查是否满足条件,这样就可以实现了。

虽然这种方式可以实现需求,但是也带来了另一个问题:线程 B 中的while()操作不会释放 CPU 资源,会导致 CPU 一直在这个方法上做判断操作,极大的浪费 CPU 资源。

我们知道 CPU 资源是非常非常昂贵的,因为使用 CPU 资源不只是当前一个应用程序,还有其它许许多多的应用程序。如果把这些轮询的时间释放出来,给别的线程使用,更能显著提升应用程序的运行效率。比如,线程 A 操作完成之后,通知线程 B 进行后续的操作,线程 B 无需通过轮询检查的方式来完成线程之间的协调,这样是不是更好。

在 Java 的父类中,也就是Object类中,就有三个方法:wait()notify()notifyAll(),它们就可以实现线程之间的通信。

如果没有接触多线程,这些方法可能基本上使用不到。下面我们一起来看看它们的使用方式!

二、方法介绍

  • wait()

wait()方法,顾名思义,表示等待的意思,它的作用是:使执行当前代码的线程进入阻塞状态,将当前线程置入"预执行队列"中,并且wait()所在的代码处停止执行,直到接到通知或被中断。

不过有个前提,在调用wait()方法之前,线程必须获得该对象的锁,因此只能在synchronized修饰的同步方法/同步代码块中调用wait()方法;同时,wait()方法执行后,会立即释放获得的对象锁以便其它线程使用,当前线程被阻塞,进入等待状态

至于wait()为什么有阻塞的效果,其内部机制非常复杂,主要由 JVM 的 C 代码实现,大家了解就行。

  • notify()

notify()方法,顾名思义,表示通知的意思,它的作用是:让处于同一监视器下的等待线程被重新唤醒,如果有多个线程等待,那么随机挑选出一个等待的线程,对其发出通知notify(),并使它等待获取该对象的对象锁。

注意“等待获取该对象的对象锁”,这意味着即使收到了通知,等待的线程也不会马上获取对象锁,必须等待notify()方法的线程释放锁才可以。

调用环境和wait()一样,notify()也要在synchronized修饰的同步方法/同步代码块中调用。

  • notifyAll()

notifyAll()方法,顾名思义,也是表示通知的意思,它的作用是:让所有处于同一监视器下的等待线程被重新唤醒,notify()方法只会随机的唤醒一个线程,而使用notifyAll()方法将一次性全部唤醒。

通常来说,notifyAll()方法更安全,因为当我们的代码逻辑考虑不周的时候,使用notify()会导致只唤醒了一个线程,而其他线程可能永远等待下去醒不过来了。

调用环境和notify()一样,notifyAll()也要在synchronized修饰的同步方法/同步代码块中调用。

三个方法总结下来就是:

  • 1.wait()方法,使线程阻塞,进入等待状态
  • 2.notify()方法,唤醒处于等待的线程,如果有多个线程就随机从中取一个
  • 3.notifyAll()方法,唤醒所有处于等待的线程
2.1、wait/notify/notifyAll 使用介绍

通常wait()方法,一般与notify()或者notifyAll()搭配使用比较多。

下面我们看一个简单的示例。

public class MyThreadA extends Thread{
   

    private Object lock;

    public MyThreadA(Object lock) {
   
        this.lock = lock;
    }

    @Override
    public void run() {
   
        synchronized (lock){
   
            System.out.println(DateUtil.format(new Date()) + " 当前线程:" + Thread.currentThread().getName() + " wait begin");
            try {
   
                // 进入阻塞等待
                lock.wait();
            } catch (Exception e) {
   
                e.printStackTrace();
            }
            System.out.println(DateUtil.format(new Date()) + " 当前线程:" + Thread.currentThread().getName() + " wait end");
        }
    }
}
public class MyThreadB extends Thread{
   

    private Object lock;

    public MyThreadB(Object lock) {
   
        this.lock = lock;
    }

    @Override
    public void run() {
   
        synchronized (lock){
   
            System.out.println(DateUtil.format(new Date()) + " 当前线程:" 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值