一 等待/通知机制介绍
1.1 不使用等待/通知机制
当两个线程之间存在生产和消费关系,也就是说第一个线程(生产者)做相应的操作然后第二个线程(消费者)感知了变化又进行相关操作。比如像下面的while语句一样,假设这个value值就是第一个线程操作的结果,都Something()是第二个线程要做的事情,当满足条件value=desire后才执行doSomething()。
while(value=desire){
doSomething();
}
但是这里有个问题就是:第二个语句不停通过轮训机制来检测判断条件是否成立。如果轮询时间的间隔太小会浪费CPU资源,轮询时间的间隔太大,就可能取不到自己想要的数据。所以这里就需要我们今天讲到的等待/通知(wait/notify)机制来解决这两个矛盾。
1.2 什么是等待/通知机制?
通俗来讲:
等待/通知机制在我们生活中比比皆是,一个形象的例子就是厨师和服务员之间就存在等待/通知机制。
- 厨师做完一道菜的时间是不确定的,所以菜到服务员手中的时间是不确定的;
- 服务员就需要去“等待(wait)”;
- 厨师把菜做完之后,按一下铃,这里的按铃就是“通知(nofity)”;
- 服务员听到铃声之后就知道菜做好了,他可以去端菜了。
用专业术语讲:
等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()/notifyAll()方法,线程A收到通知后退出等待队列,进入可运行状态,进而执行后续操作。
上述两个线程通过对象O来完成交互,而对象上wait()方法和notify()/notifyAll()方法的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
1.3 等待/通知机制的相关方法
方法名称 | 描述 |
---|---|
notify() | 随机唤醒等待队列中等待同一共享资源的 “一个线程”,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知“一个线程” |
notifyAll() | 使所有正在等待队列中等待同一共享资源的 “全部线程” 退出等待队列,进入可运行状态。此时,优先级最高的那个线程最先执行,但也有可能是随机执行,这取决于JVM虚拟机的实现 |
wait() | 使调用该方法的线程释放共享资源锁,然后从运行状态退出,进入等待队列,直到被再次唤醒 |
wait(long) | 超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回 |
wait(long,int) | 对于超时时间更细力度的控制,可以达到纳秒 |
二 等待/通知机制的实现
2.1 我的第一个等待/通知机制程序
MyList.java
public class MyList {
private static List<String> list = new ArrayList<String>();
public static void add() {
list.add("anyString");
}
public static int size() {
return list.size();
}
}
ThreadA.java
public class ThreadA extends Thread {
private Object lock;
public ThreadA(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock) {
if (MyList.size() != 5) {
Syst