等待/通知机制
等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而
执行后续操作。
上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的
关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
利用wait和notify进行交替执行
1 | public class ThreadWaitAndNotify { |
交替执行结果1
2
3
4
5
6
7
8
9
10$Runner - name:runner1,num:1
$Runner - name:runner2,num:2
$Runner - name:runner1,num:3
$Runner - name:runner2,num:4
$Runner - name:runner1,num:5
$Runner - name:runner2,num:6
$Runner - name:runner1,num:7
$Runner - name:runner2,num:8
$Runner - name:runner1,num:9
$Runner - name:runner2,num:10
锁池和等待池
每个同步对象都有自己的锁池和等待池。
锁池
假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
等待池
假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.
notify和notifyAll适用场景
如上例,使用notify且2个runnner,可认为是一个生产者一个消费者的情况下,生产和消费有条不紊的运行,没有任何问题。
- 线程调用了wait()方法,便会释放锁,并进入等待池中,不会参与锁的竞争
- 调用notify()后,等待池中的某个线程(只会有一个)会进入该对象的锁池中参与锁的竞争,若竞争成功,获得锁,竞争失败,继续留在锁池中等待下一次锁的竞争。
- 调用notifyAll()后,等待池中的所有线程都会进入该对象的锁池中参与锁的竞争。
- notify适用于所有等待的线程都是对等的(或者说唤醒顺序对其执行任务无影响),或者适用于本来就只有一个等待线程的场景。
- 只唤醒其它等待的消费者线程中的一个,如果改成notfiyAll的话就让其它线程被唤醒后立刻进入BLOCKED状态,增加了CPU的开销。
- notfiyAll适用于等待的线程有不同的目标,可以并行执行的场景。这样才不至于使所有线程被唤醒后又进入到BLOCKED状态。