在前面两篇文章当中,咱们介绍了synchronized
的基本使用和原理,可是在使用synchronized
保证数据一致性的同时,咱们但愿可以让线程之间进行一些交互逻辑,也是咱们今天要介绍的等待/通知模型,那么就须要使用到wait/notify
。bash
下面,咱们先介绍等待/通知机制的相关方法,首先要说明两点:ui
Object
定义的方法Object
对象所关联的锁,也就是说它们须要位于synchronized
修饰的同步代码块中。**(a) wait() ** 调用该方法的线程进入等待状态,并释放它所获取的对象锁,只有出现这两种状况之一,它才会从wait
方法中返回,不然将会一直处于等待状态:spa
notify / notifyAll
方法通知该线程,而且该线程获取到了对象锁(b) wait(long) / wait(long, int) 和wait
方法相同,差异是增长一种从wait
方法返回的状况:等待的时间已经到了,而且获取到了对象锁。线程
(c) notify() 通知位于等待队列中的第一个线程,使其从wait()
方法返回,而被通知的线程的继续执行须要等到它得到对象所为止。 须要注意,调用notify
方法后,并不会马上释放它所持有的对象锁,这须要等到它执行完同步代码块为止。code
(d) notifyAll() 与notify()
相似,可是它是通知全部在对象上等待的线程。对象
经过上面的介绍,咱们能够看到,在整个等待/通知机制当中,线程被挂起时主要有如下三种状态:等待状态、超时等待状态、阻塞状态,这些状态都是经过synchroized
所修饰的对象来实现的。队列
在前面咱们介绍synchronized
原理的时候,曾经说过每一个对象都会和一个Monitor
相关联,其实每一个Monitor
又包含有两个队列:等待队列和同步队列,其中等待队列中存放是进入等待状态的线程,而同步队列中存放的是等待获取锁的线程。ip
下面,咱们经过一段简单的伪代码来当即两个线程的状态转换过程:同步
synchronized public void waitThread() {
//执行a方法.
wait();
//执行b方法
}
synchronized public void notifyThread() {
//执行c方法
notify();
//执行d方法
}
复制代码
咱们有AB
两个线程,咱们模拟如下的一系列行为:it
(1) A 线程执行 waitThread 方法 此时因为对象锁没有被任何线程持有,所以,A
线程成为对象锁的持有者:
B
线程执行
notifyThread
方法时,因为此时对象锁已经被
A
线程持有,所以它被加入到同步队列中:
A
线程执行
wait
方法后,它会释放对象锁,并加入到同步队列当中,而
B
线程则成为对象锁新的持有者:
A
线程,可是此时
B
线程仍然持有对象锁,所以,
A
线程只能被加入到同步队列:
A
线程从新获取到对象锁,所以它被从同步队列中取出,继续执行接下来的逻辑:
A
线程从同步方法返回以后,那么会释放它所持有的锁
对于等待/通知模型,咱们能够总结出它的经典范式,分别针对等待方和通知方。
等待方遵循以下的原则:
wait
方法,被通知后仍然须要检查条件对应的伪代码为:
synchronized( 对象 ) {
while( 条件不知足 ) {
对象.wait();
}
对应的处理逻辑
}
复制代码
通知方遵循以下的原则:
synchronized( 对象 ) {
改变条件;
对象.notifyAll();
}
复制代码