Java等待-通知机制
现实世界中的就医流程拥有着完善的等待 - 通知机制,对比该就医流程能更好的理解和应用并发编程中的等待 - 通知机制。
基本就医流程:
- 患者去挂号,然后到就诊门口分诊,等待叫号。
- 叫到自己号时,可以找大夫就诊(获取到锁)。
- 就诊过程中医生可能会让患者去做检查,同时叫下一位患者(不满足条件释放锁,线程进入等待状态)。
- 当患者做完检查后,拿报告重新分诊等待叫号(满足条件等待获取锁)。
- 当大夫再次叫到号时患者再去找大夫就诊(获取到锁)。
用synchronized实现等待 - 通知机制,synchronized配合wait(),notify(),notifyAll()
等待队列和互斥锁是一对一的关系,每个互斥锁都有自己独立的等待队列。
在并发程序中,当一个线程进入临界区后,由于某些条件不满足,需要进入等待状态,Java对象的wait()方法就能够满足这种需求,当调用wait()方法后,当前线程就会被阻塞,并且进入到右边的等待队列当中,线程进入等待队列的同时会释放持有的互斥锁,线程释放锁之后其他线程就有机会获得锁并进入临界区了。
当线程要求的条件满足时可以用notify()和notifyAll()方法唤醒线程。
当条件满足时调用notify()会通知等待队列中的线程,告诉它条件曾经满足过。因为notify()只能保证在通知时间点,条件是满足的,被通知的线程的执行时间点和通知的时间点基本上不会吻合,所以当线程执行的时候很可能条件已经不满足了。
class Allocator{
private List<Object> als;
//一次性申请所有资源
synchronized void apply(Object from,Object to){
//经典写法
while(als.contains(from) || als.contains(to)){
try{
wait();
}catch(Exception e) {
}
}
als.add(from);
als.add(to);
}
//归还资源
synchronized void free(Object from,Object to){
als.remove(from);
als.remove(to);
notifyAll();
}
}
notify()是会随机的通知等待队列中的一个线程,而notifyAll()会通知等待队列中的所有线程,实际上使用notify(0很有风险,它的风险在于可能导致某些线程永远不会被通知到。所以尽量使用notifyAll(),除非有十足的把握.
本文章参考极客时间中王宝令老师的Java并发编程实战课程,有兴趣的可以购买该课程。