线程之间的协作

1.wait()和notify()、notifyAll()方法(这些方法均属于基类Object)

wait()函数的调用使当前线程A被阻塞以等待某个外部条件的发生(这个外部条件超出了当前方法的控制能力),通常这个条件由另外一个任务B来改变,当另外的这个任务B调用notify()或则notifyAll()时,线程A将被唤醒。所以wait()函数提供了一种不同任务之间同步的方式。

调用sleep()和yield()方法时锁并没有被释放;但是在方法里调用了wait()的时候,线程的执行将被挂起,对象的锁被释放,因为wait()将释放锁,其余的任务可以获得这个锁。

所以只能在同步控制方法或则同步控制块里调用wait()、notify()和notifyAll()方法。如果在非同步控制方法或则同步控制块里调用会抛出异常IllegalMonitorException

下面是一个示例:有两个任务,Task1将涂蜡到车上,Task2把车上所有的蜡都抛光;抛光任务只能在涂蜡任务完成之后才能进行,而在抛光任务完成之后才能进行下一次的涂蜡。

package thread;
import java.util.concurrent.*;
import static thread.Print.*;

class Car {
    //表示涂蜡抛光的处理状态
  private boolean waxOn = false;
   /**
    * 涂完了
    */
  public synchronized void waxed() {
    waxOn = true; // Ready to buff
    notifyAll();
  }
    /**
     * 抛光
     */
  public synchronized void buffed() {
    waxOn = false; // Ready for another coat of wax
    notifyAll();
  }
  public synchronized void waitForWaxing() throws InterruptedException {
    while(waxOn == false){
      wait();
    }
  }
  public synchronized void waitForBuffing() throws InterruptedException {
    while(waxOn == true){
      wait();
    }
  }
}
/**
 * 涂蜡任务
 */
class WaxOn implements Runnable {
  private Car car;
  public WaxOn(Car c) { this.car = c; }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        print("Wax On!");
          //涂蜡的过程
        TimeUnit.MILLISECONDS.sleep(200);
          //蜡涂完了,唤醒所有wait()的线程
        car.waxed();
          //等待抛光,wait()
        car.waitForBuffing();
      }
    } catch(InterruptedException e) {
      print("Exiting via interrupt");
    }
    print("Ending Wax On task");
  }
}

class WaxOff implements Runnable {
  private Car car;
  public WaxOff(Car c) { this.car = c; }
  public void run() {
    try {
      while(!Thread.interrupted()) {
          //等待涂蜡
        car.waitForWaxing();
          //涂完了
        print("Wax Off! ");
          //抛光
        TimeUnit.MILLISECONDS.sleep(200);
          //等待再涂蜡
        car.buffed();
      }
    } catch(InterruptedException e) {
      print("Exiting via interrupt");
    }
    print("Ending Wax Off task");
  }
}

public class WaxOMatic {
  public static void main(String[] args) throws Exception {
    Car car = new Car();
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.execute(new WaxOff(car));
    exec.execute(new WaxOn(car));
    TimeUnit.SECONDS.sleep(5); // Run for a while...
    exec.shutdownNow(); // Interrupt all tasks
  }
} /* Output: (95% match)
Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Exiting via interrupt
Ending Wax On task
Exiting via interrupt
Ending Wax Off task
*///:~

2. notify()与notifyAll()这两个方法区别

在实际中可能有多个任务在单个car对象上处于wait()状态,因此调用notifyAll()比调用notify()要更加安全。但是如果只有一个任务在car上处于wait()状态,就可以使用notify()来代替notifyAll()。

使用notify()只能唤醒在众多等待同一把锁的任务中的一个任务,此外为了使用notify(),所有的任务必须等待的是相同的条件,如果有多个任务等待不同的条件,就不能知道是否唤醒了恰当的任务。上面的限制众多,所以我们一般使用notifyAll()

note:notifyAll()唤醒的是“所有正在等待的任务吗?就是说程序中任何地方处于wait()状态的线程都会被唤醒吗?”,明显不是,实际上:notifyAll()因为某个特定的锁而被调用时,只有等待这个特定的锁的任务才会被唤醒。