wait()系列方法,使进入同步块的当前线程进入等待状态,返回条件是其他线程调用notify()系列函数或Interupt()系列函数,
wait()系列方法和notify()系列函数系列函数的搭配使用就可以实现线程间的顺序操作。
注意调用wait系列函数和notify系列函数的前提是获取到了对应的锁,否则是会报错的!
一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程。前者是生产者,后者是消费者。
下面介绍等待通知的经典范式:
等待方遵循如下原则:
- 获取对象的锁
- 如果条件不满足,那么调用对象的wait()方法,被通知后任然要检查条件
- 条件满足则执行对应的逻辑。
伪代码如下:
synchronized(锁对象){
while(条件不满足){
对象.wait()
}
对应的处理逻辑。。。。。
}
为何上面的原则第二条特别强调,被通知后任然要检查条件,这是因为线程调用锁对象的wait()方法后会被放置到等待队列(条件队列),其他线程调用锁对象的notify系列函数之后等待队列中的线程会被移动到阻塞队列中(尝试获取锁未获获取到的线程都被放置在阻塞队列中);所以阻塞队列中不单单有条件队列中被唤醒的线程还有获取锁失败的线程;这些线程都可以竞争锁;所以不能保证被唤醒的线程能够马上获取到锁进入同步块继续执行也就没发保证被唤醒的线程从wait()返回后之前唤醒的条件还成立,所以需要再检查。
伪代码中检查条件使用的是循环检查就是为了实现 被通知后任然要检查条件的原则,如果使用if条件检查就做不到—if检查不是循环的而是顺序的,唤醒线程从wait()方法返回后就不会再检查条件了。
通知方遵循如下原则:
- 获得对象的锁
- 改变条件
- 通知所有等待在对象上的线程
对应的伪代码如下:
synchronized(锁对象){
改变条件
对象.notifyAll();
}
为何要使用notifyAll()还是之前的道理,通知线程唤醒的线程会被从等待队列移动到阻塞队列,阻塞队列中不单单有等待线程还有通知线程,且有等待获取锁的和等待从wait返回的线程,不能保证唤醒的线程就是等待被唤醒的线程也有可能是等待进入同步队列的线程。