欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

线程等待和释放的小麻烦(wait/notify)

程序员文章站 2022-04-16 21:46:18
...

class ThreadA extends Thread{

  //线程同步的公共数据区

  Object oa=null;

  ThreadA(Object o){

  this.oa=o;

  }

  //线程A执行逻辑

  public void run(){

  //线程同步区域,需要申请公共数据的锁

  synchronized(oa){

  System.out.println("ThreadA is running......");

  for(int i=0;i

  System.out.println(" ThreadA value is "+i);

  if(i==50){

  try {

  //当前线程等待

  Thread.currentThread().wait();

  } catch (InterruptedException e) {

  e.printStackTrace();

  }

  }//if(i==50)

  }//for(int i)

  }

  }

  }

  /**

  * 线程B:等待线程A放弃锁,然后获得锁并执行,完成后唤醒线程A

  */

  class ThreadB extends Thread{

  //线程同步的公共数据区

  Object ob=null;

  ThreadB(Object o){

  this.ob=o;

  }

  //线程B执行逻辑

  public void run(){

  //线程同步区域,需要申请公共数据的锁

  synchronized(ob){

  System.out.println("ThreadB is running......");

  for(int i=0;i

  System.out.println(" ThreadB value is "+i);

  }

  

  //唤醒等待的线程

  notify();

  }

  }

  }

  //测试

  public class ThreadTest {

  public static void main(String[] args){

  Object lock=new Object(); //公共数据区

  ThreadA threada=new ThreadA(lock);

  ThreadB threadb=new ThreadB(lock);

  threada.start(); //线程A执行

  threadb.start(); //线程B执行

  }

  } 程序很简单,就是让线程A,B交替打印。但是运行的时候会抛出两个异常:

  Exception in thread "Thread-0" java.lang.IllegalMonitorStateException: current thread not owner

  Exception in thread "Thread-1" java.lang.IllegalMonitorStateException: current thread not owner

  问题就处在ThreadA中的Thread.currentThread().wait(); 和ThreadB中的notify();上。

  初学者理解wait()的时候都认为是将当前线程阻塞,所以Thread.currentThread().wairt();视乎很有道理。但是不知道大家有没有发现,在JDK类库中wait()和notify()方法并不是Thread类的,而是Object()中的。我们仔细看看wait方法的JDK文档:

  public final void wait() throws InterruptedException

  Object (Java 2 Platform SE 6)

  

  在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。

  当前线程必须拥有此 对象监视器 。该线程发布对此监视器的所有权并等待 ,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。 然后该线程将等到重新获得对监视器的所有权后才能继续执行。

  对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:

  synchronized (obj) {

  while ()

  obj.wait();

  // Perform action appropriate to condition

  }

  此方法只应由作为此对象监视器的所有者的线程来调用。

  抛出: IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。

  InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。

  看完JDK文档以后,很显然,只要把开始的程序中Thread.currentThread().wait();改成oa.wait() 。 notify();改成 ob.notify()就没有问题了。

  也就是说,只能通过同步块obj来调用wait/notify方法 ,而不能通过想当然的线程调用这两个方法。至于为什么是这样,我有一种想法,大家可以一起讨论一下:

  首先,我们都知道JVM会给每一个对象都分配唯一的一把锁。这把锁是在对象中的。

  然后,当Thread-0线程获得了这把锁后,应该是在对象中的锁内记录下当前占有自己的线程号,并把自己设置为已被占用。那么当Thread-0需要放弃锁的时候,锁对象会吧 Thread-0放入到锁的等待队列中 。而这一切和Thread-0是没有任何关系的。自然也轮不到Thread-0对象来调用某个方法来改变另一个对象中的锁(这一点也说不通,我自己的锁凭什么让你来改)。