在java方法定义上有:
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
特别地,以上方法只能在同步方法或同步块内部调用,且只能由被同步的资源对象调用。如果当前线程不是对象所得持有者,或者不是被同步的对象调用,以上方法抛出一个java.lang.IllegalMonitorStateException 异常。
下面先看一个例子:
package test;
public class WaitTest {
static public String aString="";
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized(WaitTest.aString){
try {
t1.start();
System.out.println("线程t1的状态是:"+t1.isAlive());
System.out.println("挂起主线程...");
WaitTest.aString.wait();
System.out.println("在挂起线程后面的输出");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
public void run() {
synchronized (WaitTest.aString) {
try {
Thread.sleep(1000); // 使当前线阻塞 1 s,确保主程序的 wait()执行之后再执行 notify()
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println(Thread.currentThread().getName()+" call notify()");
// 唤醒当前的wait线程
//WaitTest.aString.notify();
}
}
}
输出结果:
线程t1的状态是:true
挂起主线程...
上述结果把WaitTest.sString.notify()这段注释掉之后,挂起主线程后,主线程的WaitTest.sString.wait()后面的代码无法继续执行下去,程序不退出,无限期等待,因此,在线程wait()之后,是需要被唤醒才能执行后续代码的。
再看一个例子:
package test;
public class WaitTest2 {
public static void main(String[] args) {
ThreadA t1 = new ThreadA("t1");
synchronized(t1) {
try {
// 启动“线程t1”
System.out.println(Thread.currentThread().getName()+" start t1");
t1.start();
// 主线程等待t1通过notify()唤醒。
System.out.println("挂起主线程");
System.out.println("线程t1的状态是:"+t1.isAlive());
t1.wait(); // 不是使t1线程等待,而是让拥有t1这个对象的主线程等待
System.out.println("挂起主线程后面的输出");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadA extends Thread{
public ThreadA(String name) {
super(name);
}
public void run() {
synchronized (this) {
try {
Thread.sleep(1000); // 使当前线阻塞 1 s确保在主线程wait()之前t1没有执行完并退出
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println(Thread.currentThread().getName()+" call notify()");
// 唤醒当前的wait线程
//this.notify();
}
}
}
输出结果:
main start t1
挂起主线程
线程t1的状态是:true
挂起主线程后面的输出
在上面的例子中,同样是把notify()这个方法注释掉了,但是主线程wait()后面的代码还是可以得到执行,这说明了t1.wait()运行后,不需要t1.notify()能够唤醒!
线程正常结束后,会使以这个线程对象运行的wait()等待,退出等待状态!而如果在运行wait()之前,线程已经结束了,则这个wait就没有程序唤醒了,例如如果上述不睡1秒的话,在主线程的t1.wait()执行之前就运行完毕退出那么主线程就无法被唤醒了。
实际上,Thread源码里面的join()方法也是使用这种机制:
原码里的join()方法,实际上就是运行的 wait(). 需要运行join的线程运行join方法,实际上是在此线程上调用了需要加入的线程对象的wait()方法,加入的线程运行完后,自然从wait退出了。
结论:
线程对象的wait()方法运行后,可以不用其notify()方法退出,会在线程结束后,自动退出。