前言:
一个线程修改了一个对象的值,而另一个线程感知到变化,然后进行相关操作。前者是生产者,后者是消费者,这种模式隔离了做什么和怎么做,在功能层面上实现了解耦,体系结构上具备了良好的伸缩性。
在java中使用等待通知机制实现类似功能。
等待通知机制:
是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。两个线程通过对象O来完成交互。
对象上的wait()和notify()/notifyAll()方法如同开关信号一样,用来完成等待方和通知方之间的交互工作。
public class 等待通知机制 {
static boolean flag=true;
static Object lock=new Object();
public static void main(String[] args) throws InterruptedException {
Thread waitThread=new Thread(new Wait(),"WaitThread");
waitThread.start();
TimeUnit.SECONDS.sleep(1);
Thread notifyThread=new Thread(new Notify(),"NotifyThread");
notifyThread.start();
}
static class Wait implements Runnable{
@Override
public void run() {
synchronized (lock){
//当条件不满足时,继续wait,同时释放lock的锁
while(flag){
System.out.println(Thread.currentThread()+"flag is true.wait @"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//条件满足时,完成工作
System.out.println(Thread.currentThread()+"flag is false.running @"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
static class Notify implements Runnable{
@Override
public void run() {
synchronized (lock){
System.out.println(Thread.currentThread()+"hold lock.notify@"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag=false;
SleepUtils.second(5);
}
synchronized (lock){
System.out.println(Thread.currentThread()+"hold lock again.sleep @"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
SleepUtils.second(5);
}
}
}
}
/* * WaitThread首先获取了对象的锁,然后调用对象的wait()方法, * 从而放弃了锁并进入了对象的等待队列WaitQueue中,进入等待状态 * 由于WaitThread释放了对象的锁,NotifyThread随后获取了对象的锁,并调用对象的notyfy()方法, * 将WaitThread从WaitQueue移动到SynchronizedQueue中 * 此时WaitThread的状态变成阻塞状态。 * NotifyThread释放了锁之后,WaitThread再次获取到锁并从wait()方法返回并继续执行 * */
运行结果:
代码说明:
(1)使用wait(),notify()和notifyAll()需要先对调用对象加锁
(2)调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放到对象的等待队列中
(3)notify()或者notifyAll()方法调用后,等待线程不会从wait()返回,需要调用notify()或者notifyAll()的线程释放锁后,等待线程才有机会从wait()返回。
(4)notify()方法将等待队列的一个等待线程从等待队列移到同步队列中,而notifyAll()方法将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为BLOCKED。
(5)从wait()方法返回的前提是获得了调用对象的锁。
等待/通知线程依赖于同步机制,目的是确保等待线程从wait()方法返回时能够感知到通知线程对变量做出的修改。
等待/通知的经典范式:
范式分为两个部分,分别针对等待方(消费者)和通知方(生产者):
等待方(消费者)遵循以下原则:
(1)获取对象的锁
(2)如果条件不满足,那么调用对象的wait方法,被通知后仍要检查条件
(3)条件满足则执行对应的逻辑
伪代码:
synchronized(对象)
{
while(条件不满足){
对象.wait();
}
对应的处理逻辑
}
通知方(生产者)遵循如下原则:
(1)获得对象的锁
(2)改变条件
(3)通知所有等待在对象上的线程
synchronized(对象){
改变条件
对象.notifyAll();
}