Written by
tt-yy
on
on
java wait notify
wait、notify、notifyAll都是Object的方法,主要用于多线程环境中对线程进行协调。在使用时必须包含在同步代码块中(如:synchronized)且所属的锁资源必须相同,否则将抛出IllegalStateException。
下面对这3个方法进行介绍:
- wait 使当前线程等待,同时释放当前获得的锁资源,使其它线程可以获取锁资源并进行相关操作,直到其它线程调用了notify或notifyAll之后再唤醒该线程,唤醒之后将重新获取锁资源并继续执行。
- notify 将一个当前处于等待状态中且有相同锁资源的线程唤醒。如果有多个线程处于等待状态,将随机唤醒其中的一个。需要注意的是,被唤醒的线程并不会立即执行,而要等当前线程释放了锁资源后且重新获取到了锁资源时才能执行。
- notifyAll 将所有处于等待状态中且有相同锁资源的线程唤醒,其它功能与notify相同。
了解完基本原理后,咱们开始写个示例并解决实际问题。
在多线程开发过程中,经常需要对多个线程的执行先后顺序进行控制,比如:T1执行完成后,T2开始执行。根据这个需求,编写一个多线程打印程序,启动2个线程并发执行,根据指定顺序打印字符串。比如:T1打印abc后,T2再打印def。以下是实现代码:
/**
* @author TTY.
*/
public class PrintWithOrder3 {
// Defines an object for acquire lock
private final Object monitor;
// A condition for checks wait or continue
private boolean enabled = false;
public PrintWithOrder3() {
this.monitor = new Object();
}
public void printFirst() {
synchronized (monitor) {
System.out.print("abc");
enabled = true;
monitor.notify();
}
}
public void printTwo() throws InterruptedException {
synchronized (monitor) {
/*
* As in the one argument version of wait, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* */
while (!enabled) {
monitor.wait();
}
System.out.print("def");
}
}
public static void main(String[] args) {
PrintWithOrder3 pwo = new PrintWithOrder3();
Runnable first = pwo::printFirst;
Runnable two = () -> {
try {
pwo.printTwo();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
new Thread(first).start();
new Thread(two).start();
}
}
运行后,不论first或two的线程顺序如何,程序将始终根据顺序进行打印abcdef。下面对代码的运行情况进行分析:
- 当first线程先获得monitor的锁后,将打印abc,同时执行notify进行唤醒操作,这里的notify操作其实没有作用(因为first先获得了锁且还未释放,所以two线程还在等待获取锁),当first线程释放锁后,two线程开始执行,这里有个状态变量用于判断是否需要执行wait方法,因为first执行完后将变量设置成了true,所以这里不会执行wait,程序继续执行并打印def
- 当two线程先获得monitor的锁后,状态变量为false,所以这里会执行wait进入等待状态,同时让出锁资源,这样first线程就可以获得锁资源并进入同步块代码执行。first执行打印abc,设置状态变量并执行notify方法将two线程唤醒,但是two线程不会马上执行,因为first线程还未释放锁。当first线程释放锁资源后,two线程将重新获得锁资源(重新获得锁资源并不是从头执行一次synchronized,而是jvm内部自动设置的,此时代码将从monitor.wait后执行)并继续执行,最后打印def
根据wait方法的api注释:wait可能会被interrupts或虚假的唤醒,所以在使用时,建议始终根据条件变量来进行判断是否需要wait,比如:当two线程处于等待状态而被中断时,使用while结合条件判断,则可以使线程继续等待;还比如:first先执行时,如果没有条件变量,two线程将永远无法被唤醒。因为first先获得了锁且还未释放,而two线程还在等待获取锁,没有进入同步代码块。当first执行完后,two线程进入同步代码块开始执行,执行到wait后会进入等待状态,而first线程早已执行完成,所以不会有notify发出了,这里就会永远等待。
以下为jdk api建议部分:
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}