background image

 
线程在 synchronized 语句获取对象的内部锁之后,在 synchronized 代码块期
间就拥有了内部锁。当判断条件不成立时,可以调用该对象的 wait 方法进入等待
状态。
  注意持有锁的线程在调用 wait 方法进入等待状态之后,会自动释放持有的锁。这样
做的目的是允许其他的线程进入临界区继续操作,以防止死锁的发生。
  举生产者和消费者的例子。如果消费者在检查时发现没有产品生成,则调用 wait 方
法等待生产者生产。如果此时消费者不释放该锁,生产者就会因为获取不到该锁而处于阻
塞状态。而此时消费者却在等待生产者生产出产品来,这样双方就进入死锁状态。因此
wait 方法需要在挂起线程后释放该线程所拥有的锁。
  当 wait 方法调用后,线程进入等待状态,直至未来某刻其他线程获得该锁并调用其
invokeAll(或 invoke)方法将其唤醒。该线程通过如下类似的代码激活等待在此锁上的线
程:
 public synchronized notifyOperation(){
  condition_expression=true;
  notifyAll();
  }
  假设线程 C 因检测到某种条件不满足而进入等待状态,激活 C 线程的 P 线程往往需
要和 C

线程建立 发生过 关系。也就是说程序期望线程 P 和 C 之间按照先 P 后 C 的顺序

执行。对于生产者和消费者例子来说,P 就是生产者,C 就是消费者,它们之间存在从 P
到 C

的 发生过 关系。

  线程 P 在调用 notify 或者 notifyAll 方法时需要首先获得该对象的锁,因此这些代码
也需要放在 synchronized 代码体内。上面的激活方法更通用的写法是:
...
 synchronized(lock){
 condition_expression=true;
 lock.notifyAll();
 }
  ...
  现举生产者和消费者之间同步的例子。为了简化,假设生产者和消费者之间只共享一
个容器。生产者生产出对象后放在在该容器中,而消费者从该容器中获取该对象进行消费。

消费者和生者之间往往需要建立双向的 发生过 关系,即消费者只有在有东西才能消费,
而生产者只有在有存放空间时才能生产。这儿为了简化,只假定保证消费者有东西可消费,
生产者不管是否有空间可存放,只是将对象生产出来放在容器中。下面是这个例子的代码:
public class TankContainer{
 private Tank tank;
 public synchronized void putTank(Tank tank){
 //Dont bother to check whether it has room.
 this.tank=tank;
   notifyAll();
  }
  public synchronized Tank getTank(){
  //Check whether there's tank to consume