线程的等待-通知机制
一. 前言
在之前讲到如何解决死锁问题的时候,其中有一个方法就是破坏占有且等待条件,方法中讲到是一次性申请所有资源就能解决问题,但是其中有一个点需要注意,除了那个申请到资源的线程可以继续执行外,其它线程都是只能不断地循环来判断资源是否已经释放了,这样的结果就是很多 CPU 资源都浪费在了循环判断上了。
在之前讲到如何解决死锁问题的时候,其中有一个方法就是破坏占有且等待条件,方法中讲到是一次性申请所有资源就能解决问题,但是其中有一个点需要注意,除了那个申请到资源的线程可以继续执行外,其它线程都是只能不断地循环来判断资源是否已经释放了,这样的结果就是很多 CPU 资源都浪费在了循环判断上了。
/**
* 获取所有资源
*/
synchronized boolean getAll(Object coffeeBean, Object kettle) {
if (list.contains(coffeeBean) || list.contains(kettle)) {
System.out.println(Thread.currentThread().getName() + " 尝试获取所有资源");
return false;
} else {
list.add(coffeeBean);
list.add(kettle);
System.out.println(Thread.currentThread().getName() + " 获取咖啡豆和水壶成功!");
}
return true;
}
// 如果没有满足条件则一直循环
while (!distributor.getAll(coffeeBean, kettle));
而线程中还有一个更好的解决办法,来避免无谓的循环,就是等待-通知机制。机制中会涉及到常见的 wait()
、notify()
、notifyAll()
方法。
二. 等待-通知机制的简单原理
这套机制的运作方法理解起来也比较简单,就是如果一个线程申请不到资源,那就使用 wait()
方法将线程从 RUNNABLE 状态转为 WAITING 状态,也就是让线程放到对象相对应的等待队列中等待。如果这时候有一个线程完成了,就需要使用 notify()
方法来通知等待队列中的一个等待的线程移到同步队列中来接下去执行。
notifyAll()
和 notify()
的区别就是 notifyAll()
会通知所有线程,而 notify()
只会通知其中一个,所以一般来说,推荐使用 notifyAll()
,避免有的线程永远通知不到的情况发生。
三. 用等待-通知机制优化代码
将前面的例子用等待-通知机制优化后可以得到下面的代码:
public class Distributor {
private List<Object> list = new ArrayList<>();
/**
* 获取所有资源
*/
synchronized void getAll(Object coffeeBean, Object kettle) {
while (list.