java wait notify

wait、notify、notifyAll都是Object的方法,主要用于多线程环境中对线程进行协调。在使用时必须包含在同步代码块中(如:synchronized)且所属的锁资源必须相同,否则将抛出IllegalStateException。

下面对这3个方法进行介绍:

了解完基本原理后,咱们开始写个示例并解决实际问题。

在多线程开发过程中,经常需要对多个线程的执行先后顺序进行控制,比如:T1执行完成后,T2开始执行。根据这个需求,编写一个多线程打印程序,启动2个线程并发执行,根据指定顺序打印字符串。比如:T1打印abc后,T2再打印def。以下是实现代码:

/**
 * @author TTY.
 */
public class PrintWithOrder3 {

  // Defines an object for acquire lock
  private final Object monitor;

  // A condition for checks wait or continue
  private boolean enabled = false;

  public PrintWithOrder3() {
    this.monitor = new Object();
  }

  public void printFirst() {
    synchronized (monitor) {
      System.out.print("abc");
      enabled = true;
      monitor.notify();
    }
  }

  public void printTwo() throws InterruptedException {
    synchronized (monitor) {
      /*
      * As in the one argument version of wait, interrupts and spurious wakeups are
      * possible, and this method should always be used in a loop:
      *  */
      while (!enabled) {
        monitor.wait();
      }
      System.out.print("def");
    }
  }

  public static void main(String[] args) {
    PrintWithOrder3 pwo = new PrintWithOrder3();
    Runnable first = pwo::printFirst;
    Runnable two = () -> {
      try {
        pwo.printTwo();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    };
    new Thread(first).start();
    new Thread(two).start();

  }
}

运行后,不论first或two的线程顺序如何,程序将始终根据顺序进行打印abcdef。下面对代码的运行情况进行分析:

根据wait方法的api注释:wait可能会被interrupts或虚假的唤醒,所以在使用时,建议始终根据条件变量来进行判断是否需要wait,比如:当two线程处于等待状态而被中断时,使用while结合条件判断,则可以使线程继续等待;还比如:first先执行时,如果没有条件变量,two线程将永远无法被唤醒。因为first先获得了锁且还未释放,而two线程还在等待获取锁,没有进入同步代码块。当first执行完后,two线程进入同步代码块开始执行,执行到wait后会进入等待状态,而first线程早已执行完成,所以不会有notify发出了,这里就会永远等待。

以下为jdk api建议部分:

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait();
    ... // Perform action appropriate to condition
}