每个运行中的线程,如果仅仅是孤立地运行,那么没有太大的价值,但如果多个线程能够相互配合完成工作,这将会带来巨大的价值。Java多线程的等待/通知机制就是用来完成线程之间的通信。
介绍
一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作。
-
前者是通知方(生产者),后者是等待方(消费者)。
-
整个过程开始于一个线程,而最终执行又是另一个线程,从而在功能上实现了解耦,使得体系结构上具备了良好的伸缩性。
实现方式
等待/通知机制依托于同步机制,其目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量的修改。
等待方的原则:
- 1) 获取对象的锁
- 2)如果条件不满足,那么调用锁的wait()方法,使该线程进入waiting,被通知后依然要检查条件
- 利用while循环是为了确保安全性。因为即使条件没被满足,线程仍有可能被错误地唤醒 :
- 在某个线程调用notify到等待线程被唤醒的过程中,有可能出现另一个线程得到了锁并修改了条件使得条件不再满足。
- 条件不满足,但另一个线程意外地调用了notify。
- 只有某些等待线程的条件满足了,但通知线程调用了notifyAll。
- 有可能出现“伪唤醒”。
- 利用wait()方法是为了防止死循环使得CPU占用率过高。
- 条件满足则执行对应的逻辑
伪代码:
synchronized(对象){
while(条件不满足){
对象.wait();
}
对应的逻辑处理
}
通知方的原则:
- 1) 获取对象的锁
- 2)改变条件
- 3)通知所有等待在该对象上的线程
伪代码:
synchronized(对象){
改变条件
对象.notifyAll();
}
wait
- wait使线程停止运行,notify使停止的线程继续运行。
wait()
:将当前执行代码的线程进 入WAITING
状态- 在调用wait()之前,线程必须获得该对象的对象级别锁;
- 执行wait()方法后,当前线程立即释放锁;
- 从wait()返回前,线程与其他线程竞争重新获得锁
- 当线程呈wait()状态时,调用线程的interrup()方法会出现InterrupedException异常
wait(long)
是等待某一时间内是否有线程对锁进行唤醒,超时则自动唤醒。
notify()
- 通知可能等待该对象的对象锁的其他线程。由JVM(与优先级无关)随机挑选一个呈wait状态的线程。
- 在调用notify()之前,线程必须获得该对象的对象级别锁;
- 执行完notify()方法后,不会马上释放锁,要直到退出synchronized代码块,当前线程才会释放锁。
- notify()一次只随机通知一个线程进行唤醒
notifyAll()
- 和
notify()
差不多,只不过是使所有正在等待池中等待同一共享资源的全部线程从等待状态退出,进入可运行状态。- 让它们竞争对象的锁,只有获得锁的线程才能进入就绪状态;
- 每个锁对象有两个队列:就绪队列和阻塞队列。
- 就绪队列:存储将要获得锁的线程
- 阻塞队列:存储被阻塞的的线程