简介
wait、notify、notifyAll是Java中3个与线程有关的方法,它们都是Object类中的方法。
其中,wait方法有3个重载形式:
1、wait()
2、wait(long timeout)
3、wait(long timeout, int nanos)
这5个方法都是final方法。其中,wait(long timeout)、notify()、notifyAll()都是native方法。
另外,3个wait方法均有InterruptedException抛出,而notify和notifyAll方法没有异常抛出。
这3类方法的语义大致如下所述:
wait |
如果调用了某对象的wait方法,那么持有该对象的锁的线程会放弃对该对象的锁定,然后等待再次获得该对象的锁。 |
notify |
如果调用了某对象的notify方法,那么某个在等待获得该对象的锁的线程会被唤醒继续执行。 |
notifyAll |
与notify稍有不同的是,notifyAll方法会唤醒在该对象上等待的所有线程。 |
特别提醒的是,wait、notify、notifyAll方法都必须在同步方法或同步语句块内部调用。这是因为要调用这3类方法,必须让当前线程持有方法所属的对象的锁,否则就会抛出IllegalMonitorStateException。
例如,下面的代码是错误的,因为在调用对象a的wait()方法之前,当前线程没有持有对象a的锁:
public class A { public void m() throws InterruptedException { this.wait(); } public static void main(String[] args) { A a = new A(); try { a.m(); } catch (Throwable t) { t.printStackTrace(); } } } |
运行上面的代码,必然抛出如下异常:
java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:485) at A.m(A.java:4) at A.main(A.java:10) |
正确的写法,是给A类的m方法加上synchronized修饰符,这样,当执行到m方法内部的“this.wait()”语句时,当前线程已经持有了对象a的锁,调用它的wait()方法就不会抛出异常了。
有了这些知识点,我们来具体看一下这3类方法。
wait(long timeout)
该方法的声明语句如下:
public final native void wait() throws InterruptedException; |
它的作用是:调用某对象的wait(long timeout)方法会造成当前线程放弃该对象的锁,转到等待状态,直到其它线程调用了该对象的notify()或notifyAll()方法,或者指定的时间(timeout)被耗尽。
该方法的参数timeout的单位是毫秒。值得注意的是,即便timeout参数所指定的时间被耗尽,当前线程也不是一定就会恢复执行,这要看时间耗尽时是否有其它线程持有该对象的锁。
参数timeout不能是负数,否则会抛出IllegalArgumentException。timeout为0时,表示无限等待,时间不会耗尽,只能等别的线程来唤醒自己。
wait()、wait(long timeout, int nanos)
这两个方法的声明语句如下:
public final void wait() throws InterruptedException { wait(0); } public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) throw new IllegalArgumentException( “timeout value is negative”); if (nanos < 0 || nanos > 999999) throw new IllegalArgumentException( “nanosecond timeout value out of range”); if (nanos >= 500000 || (nanos != 0 && timeout == 0)) timeout++; wait(timeout); } |
从代码上看,并结合wait(long timeout)的介绍,其逻辑意义就很明显了,不多做介绍。
notify()、notifyAll()
这两个方法的声明语句如下:
public final native void notify(); public final native void notifyAll(); |
notify和notifyAll的区别是,notifyAll会唤醒所有在某对象上等待的线程继续执行,但实际上,无论调用notify还是调用notifyAll,结果都只能有一个线程拿到锁并真的继续执行。
需要注意的是,当notify或notifyAll被调用成功后,被唤醒的线程实际上已经进入synchronized方法或代码块内部,这一点非常重要。