一、等待/通知机制的简介
线程之间的协作:
为了完成某个任务,线程之间需要进行协作,采取的方式:中断、互斥,以及互斥上面的线程的挂起、唤醒;如:生成者–消费者模式、或者某个动作完成,可以唤醒下一个线程、管道流已准备等等;
等待/通知机制:
等待/通知机制 是线程之间的协作一种常用的方式之一,在显示锁Lock 和 内置锁synchronized都有对应的实现方式。
等待/通知机制 经典的使用方式,便是在生产者与消费者的模式中使用:
1、生产者负责生产商品,并送到仓库中存储;
2、消费者则从仓库中获取商品,享受商品;
3、仓库的容量有限,当仓库满了后,生产者就会停止生产,进入等待状态。直到消费者消耗了一部分商品,通知生产者,仓库有空位了,生产者才会继续生产商品;
4、当消费者的消耗速度过快,消耗光仓库的商品时,也会停止消耗,进入等待状态,直到生产者生产了商品,并通知唤醒消费者时,消费者才继续消耗商品。
二、等待/通知机制 在synchronized下的实现:
在内置锁下,等待/通知机制 是依赖于wait( )和 notify、notifyAll 来实现的,这三个方法都是继承自根类Object中。
下面是这几个方法的API描述,wait()方法有3个版本:
void wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void wait(long timeout): 超时等待,在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。如果时间当时间到来了,线程还是没有被唤醒或中断,那么就会自动唤醒;
void wait(long timeout, int nanos): 参数timeout的单位是毫秒,参数nanos的单位是纳秒,即等待时间精确到纳秒。其他特性与wait(long timeout)一样。
void notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
void notifyAll():唤醒在此对象监视器上等待的所有线程。
1、调用 wait、notify、notifyAll 前,必须先获取锁
在调用 wait、notify、notifyAll 前,必须获取锁(对象监视器),而且这三个方法是由synchronized的对象锁(对象监视器)来调用。对象监视器 中维护着两个队列:就绪队列,等待队列。调用了wait()方法就是使线程进入等待队列,不再参与锁的竞争,直到被唤醒才能重新进入就绪队列,才可能获取锁,再次被执行。
如果在没有获取锁(对象监视器)的情况下,将会抛出IllegalMonitorStateException 异常:
wait、notify、notifyAll 方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:
- 通过执行此对象的同步实例方法。
- 通过执行在此对象上进行同步的 synchronized 语句的正文。
- 对于 Class 类型的对象,可以通过执行该类的同步静态方法。
一次只能有一个线程拥有对象的监视器。
抛出:
IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。