如果多个线程能够相互配合完成工作,这将会带来巨大的价值。

java多线程的等待和通知机制就是用来完成线程之间的通信。

实现这种机制最简单的方法就是让消费者线程不断的循环检查变量是否符合预期,如下代码所示:

while(value!=desire){
   xxx.wait(); 
}
doSomething();

在while循环中设置不满足条件的条件,如果条件满足则退出while循环,从而完成消费者的工作。让消费者线程wait的作用是让其进入WAITING状态,当生产者线程更新的value值的时候就进行notify唤醒该线程,让消费者线程完成doSomething.

示例代码:

public class WaitNotify{
  static boolean flag = true;
  static Object lock = new Object();

 public static void main(String[] args) throws Exception{
           Thread waitThread = new Thread(new Wait(),"WaitThread");
           waitThread.start();
           TimeUtil.SECONDS.sleep(1);
           Thread notifyThread = new Thread(new Notify(),"NotifyThread");
           notifyThread.start();
    }

static class  Wait implements Runnable{
   public void run(){
      //加锁
      synchronized(lock){
       //当条件不满足的时候,进入WAITTING状态,同时释放lock锁
       while(flag){
        System.out.println("flag is true ");
          lock.wait();
          }
        //条件满足
        System.out.println("doSomething");
      }
    }
}
 static class  Notify implements Runnable{
   public void run(){
      //加锁
      synchronized(lock){
         //获取lock的锁,然后进行通知,通知不会释放lock锁
         //直到发出通知的线程执行完毕释放了lock锁,WaitThread线程才能从wait方法返回
         lock.notifyAll();
          System.out.println("flag is false now");
         flag = false;
      }
   }
}

输出内容如下:

flag is true
 flag is false now
 doSomething

现在我们来看调用 wait(),notify(),notifyAll()的注意事项:

  • 调用 wait(),notify(),notifyAll()时需要先对调用对象加锁。
  • 调用wait()方法后,线程由RUNNING变为WAITING,并将当前线程放置于对象等待队列
  • notify(),notifyAll()方法被调用后,等待线程依然不会从wait()方法返回,而是等调用notify(),notifyAll()的线程释放该锁之后,等待线程才有机会从wait()返回。
  • notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而 notifyAll()方法则是把所有等待线程从等待队列中移到同步队列中,被移动的线程的状态由WAITING变成BLOCKED
  • 从wait()方法返回的前提是获得了调用对象的锁。

等待/通知机制依托于同步机制,其目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量的修改。

等待/通知的经典范式

我们在如上的示例代码中提炼出等待/通知的经典范式,该范式分为两部分,分别为等待方(消费者)和通知方(生产者)

等待方的原则:

  • 1) 获取对象的锁
  • 2)如果条件不满足,那么调用锁的wait()方法,使该线程进入waiting,被通知后依然要检查条件
  • 条件满足则执行对应的逻辑

伪代码:

synchronized(对象){
    while(条件不满足){
     对象.wait();
  }
  对应的逻辑处理
}

通知方的原则:

  • 1) 获取对象的锁
  • 2)改变条件
  • 3)通知所有等待在该对象上的线程

伪代码:

synchronized(对象){
     改变条件
     对象.notifyAll();
}