要点总结:
1、wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。
2、在当前线程拥有监视器锁的情况下使用,且wait()的对象和锁对象相同,否则将抛出异常IllegalMonitorStateException。一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。
3、wait() 使当前线程阻塞,并且会释放synchronized锁。被wait阻塞的线程,可以被notify/notifyAll()唤醒,重新获取锁后,可以从上一次wait()处继续往下执行。
4、wait() 可以被中断,需要处理中断异常。如果捕获但不处理,即:e.printStackTrace();将会抛出异常到jvm并结束异常线程;如果处理了异常,将会从catch{}后继续往下执行。("等待"状态被中断,就该就绪-运行状态了。)
5、notify() 可以唤醒一个受wait()阻塞的线程,唤醒哪一个,取决于系统,但被唤醒的线程还需要前提:能获取到synchronized锁才可以执行,因此当有多个线程wait住,使用notify() 可能出现死锁。
6、notifyAll() 可以唤醒全部受wait()阻塞的线程,被唤醒的线程还需要前提:能获取到synchronized锁才可以执行,因此notifyAll() 唤醒的线程并不是立刻都会被执行,能否执行、执行的顺序根据争抢锁的情况决定。
7、notify() /notifyAll() 可以唤醒受wait()阻塞的线程,但notify() 不会释放synchronized锁,只有synchronized锁住的代码块都执行完才会释放,所以 notify()/notifyAll() 一般写在代码块最后,唤醒后随即释放锁。
8、wait() 与notify()/notifyAll() 在运行时有顺序要求,如果A线程先执行notify()/notifyAll() 方法,B线程再执行wait方法,那么B线程是无法被唤醒的。所以使用notifyAll() 也可能出现死锁。
9、受wait()阻塞的线程被唤醒后,会从上一次wait()处继续往下执行。所以在利用共享变量进行条件判断时候,在阻塞与唤醒这期间,共享变量可能已经被其他线程修改过了,不一定再满足当前线程可执行条件,应该使用while循环判断而不是if。
wait状态的中断
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("我要一直等待被唤醒...");
obj.wait();
System.out.println("我被正常唤醒了...");
} catch (InterruptedException e) {
System.out.println("我被打断了...");
// e.printStackTrace();
}
System.out.println("我继续执行了...");
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1L); // 确保线程已经wait,再将他interrupt
thread.interrupt();
输出:
我要一直等待被唤醒...
我被打断了...
我继续执行了...
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
while (list.size() <= 0) {
try {
System.out.println("我要一直等待被唤醒...");
obj.wait();
System.out.println("我被正常唤醒了...");
} catch (InterruptedException e) {
System.out.println("我被打断了...");
// e.printStackTrace();
}
}
System.out.println("我继续执行了...");
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1L); // 确保线程已经wait,再将他interrupt
thread.interrupt();
输出:
我要一直等待被唤醒...
我被打断了...
我要一直等待被唤醒...
| ----(被下一次循环wait住)
生产/消费者模式
// 生产者线程
Thread a = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
while (list.size() >= 10) { // 有10个待消费的消息就暂停生产
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add("A");
System.out.println("生产者A 生产了一个消息....");
obj.notifyAll();
}
}
}, "生产者A");
// 消费者线程
Thread c = new Thread(new Runnable() {
@Override
public void run() {
while (true) { // 循环是让消费者可以一直收消息 --方便测试用
synchronized (obj) {
while (list.size() <= 0) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String msg = list.removeFirst();
System.out.println("消费者C 消费了一个消息...." + msg);
obj.notifyAll();
}
}
}
}, "消费者C");
c.start();
TimeUnit.SECONDS.sleep(1L);
a.start();
输出:
生产者A 生产了一个消息....
消费者C 消费了一个消息....A