在执行wait()和notify()之前,必须要先获得互斥锁,即一定要和synchronized一起使用。wait()的含义是让出获得的互斥锁,并让自己进入阻塞状态。在notify()的时候也已经获得了互斥锁,所做的事情就是唤醒当前线程继续执行。
假如synchronized的锁对象是obj的话,wait和notify正确的使用方法是obj.wait()和obj.notify()。如果使用this作为锁,则可以直接写成wait()和notify()。如果前后使用的锁对象不一致,会发生IllegalMonitorStateException。
当有多个线程共同使用一个互斥锁时,notify()会随机选取一个执行过wait()的线程唤醒,其余会继续保持阻塞状态。如果想唤醒所有阻塞的进程,就使用到了notifyAll()。
有点晕了么,把代码拿去执行可以好好体会下,一会看完代码,看结论
package com.javaer.thread;
public class Twait {
public static void main(String[] args) {
TestThread testThread1 = new TestThread();
TestThread testThread2 = new TestThread();
TestThread testThread3 = new TestThread();
testThread1.start();
testThread2.start();
testThread3.start();
System.out.println("主线程休眠5秒");
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
System.out.println("主线程 Interrupted");
}
System.out.println("唤醒 线程Thread-0");
testThread1.resumeByNotify();
try {
System.out.println("主线程再次休眠");
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
System.out.println("Main Thread Interrupted");
}
System.out.println("唤醒所有 By NotifyAll");
testThread1.resumeByNotifyAll();
}
}
class TestThread extends Thread {
private static Object obj = new Object();
@Override
public void run() {
System.out.println(getName() + " 即将进入阻塞");
synchronized (obj) {
try {
obj.wait();
} catch (InterruptedException e) {
System.out.println(getName() + " Test Thread Interrupted");
}
}
System.out.println(getName() + " 被唤醒");
}
public void resumeByNotify() {
synchronized (obj) {
obj.notify();
}
}
public void resumeByNotifyAll() {
synchronized (obj) {
obj.notifyAll();
}
}
}
Thread-0 即将进入阻塞
Thread-2 即将进入阻塞
主线程休眠5秒
Thread-1 即将进入阻塞
唤醒 线程Thread-0
主线程再次休眠
Thread-0 被唤醒
唤醒所有 By NotifyAll
Thread-1 被唤醒
Thread-2 被唤醒
上面的例子,子线程启动了,就开始阻塞,然后主线程一个个的唤醒。没有线程唤醒,这个子线程将一直等待。
testThread1.resumeByNotifyAll();
注释这句话,程序将僵持在那里。传说中的僵尸。
Thread-0 即将进入阻塞
Thread-2 即将进入阻塞
主线程休眠5秒
Thread-1 即将进入阻塞
唤醒 线程Thread-0
主线程再次休眠
Thread-0 被唤醒
唤醒所有 By NotifyAll
到了这里卡住了。
结论
1.wait 当前线程因为某种原因需要进入阻塞状态,即线程暂停
2.notify 唤醒一个阻塞的线程即被wait的
3.notifyall 唤醒所有阻塞线程。
在调用wait的时候,线程自动释放其占有的对象锁,同时不会去申请对象锁。当线程被唤醒的时候,它才再次获得了去获得对象锁的权利。