wait(...) notify() notifyAll()

时间:2022-08-12 02:12:58

简介

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方法或代码块内部,这一点非常重要。