前言:

一个线程修改了一个对象的值,而另一个线程感知到变化,然后进行相关操作。前者是生产者,后者是消费者,这种模式隔离了做什么和怎么做,在功能层面上实现了解耦,体系结构上具备了良好的伸缩性。

在java中使用等待通知机制实现类似功能。

等待通知机制:

是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。两个线程通过对象O来完成交互。

对象上的wait()和notify()/notifyAll()方法如同开关信号一样,用来完成等待方和通知方之间的交互工作。

java并发编程笔记:等待/通知机制_等待队列

 

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()方法返回并继续执行 * */


 

运行结果:

java并发编程笔记:等待/通知机制_等待队列_02

 代码说明:

(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();
}