Java线程的等待与唤醒主要包括几个方法:

(1)notify():唤醒在此对象监视器上等待的单个线程。

(2)notifyAll():唤醒在此对象监视器上等待的所有线程。

(3)wait():让当前线程处于阻塞状态,同时释放它所持有的锁。

(4)wait(long timeout):让线程处于阻塞状态,直到其他线程调用此对象的notify()或者notifyAll()方法,或者超过指定的时间量,当前线程被唤醒。

(5)wait(long timeout,int nanos):同上。

wait()和notify()函数示例:


class ThreadA extends Thread{

public ThreadA(String name) {
super(name);
}
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName()+" call notify()");
// 唤醒当前的wait线程
notify();
}
}
}
public class WaitTest {

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(Thread.currentThread().getName()+" wait()");
t1.wait();
System.out.println(Thread.currentThread().getName()+" continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


运行结果:

main start t1
main wait()
t1 call notify()
main continue

其实t1.wait()的意思含义是停止当前的线程,当然就是CPU上正在运行的程序,这样整个的结果输出就比较合理,t1在运行run()函数会运行notify(),main线程开始重新运行。

wait(long timeout)和notify()函数:

示例程序:

// WaitTest.java的源码
class ThreadA extends Thread{

public ThreadA(String name) {
super(name);
}

public void run() {
System.out.println(Thread.currentThread().getName() + " run ");
// 死循环,不断运行。
while(true);
}
}

public class WaitTimeoutTest {

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()唤醒 或 notifyAll()唤醒,或超过3000ms延时;然后才被唤醒。
System.out.println(Thread.currentThread().getName() + " call wait ");
t1.wait(3000);
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

运行结果:

main start t1

main call wait

t1 run                  // 大约3秒之后...输出“main continue”

main continue

在运行t1.wait(3000),main线程会让出3秒CPU,之后会重新竞争资源。


wait()和notifyAll()方法

示例代码:

package test;

public class NotifyAllTest {

private static Object obj = new Object();
public static void main(String[] args) {

ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
ThreadA t3 = new ThreadA("t3");
t1.start();
t2.start();
t3.start();
try {
System.out.println(Thread.currentThread().getName()+" sleep(3000)");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(obj) {
// 主线程等待唤醒。
System.out.println(Thread.currentThread().getName()+" notifyAll()");
obj.notifyAll();
}
}
static class ThreadA extends Thread{

public ThreadA(String name){
super(name);
}
public void run() {
synchronized (obj)
{
try {
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " wait");
// 唤醒当前的wait线程
obj.wait();
// 打印输出结果
System.out.println(Thread.currentThread().getName() + " continue");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

第一次运行结果:

t2 wait

main sleep(3000)

t1 wait

t3 wait

main notifyAll()

t3 continue

t1 continue

t2 continue

第二次运行结果:

main sleep(3000)

t2 wait

t3 wait

t1 wait

main notifyAll()

t1 continue

t3 continue

t2 continue

刚开始每个线程都运行wait将其他线程阻塞,最后main线程重新获得资源,运行notify()函数,唤醒所有的线程。

为什么notify(),wait()等函数定义在Object中,而不是Thread类中。

Object中的wait(),notify()等函数,和synchronized一样,会对对象的同步锁进行操作。wait()会使当前线程处于阻塞状态,是因为该线程进入了阻塞状态,所以线程应该释放了它拥有的同步锁,否则其他线程无法运行。wait()等待线程和notify()之间是通过什么关联起来的?答案是对象的同步锁。负责唤醒等待线程的那个线程,只有它在获取了该对象的同步锁之后,才调用notify或notify()方法之后,才能唤醒等待线程。

总之,notify(),wait()依赖于同步锁,而同步锁是对象锁持有,并且每个对象有且仅有一个,这就是为什么notify()和wait()等函数定义在object类,而不是Thread类中的原因。