目录
前言
上一篇文章说了Thread.sleep()背后的原理和各种案例,今天我们来研究一下java的另外一种使线程休眠的方法Object.wait()。
一、看看JDK的注释上都说了什么
/**
* Causes the current thread to wait until another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
翻译:此方法会导致当前线程进入 wait 状态,直到其他线程调用了notify() 方法或者notifyAll()方法
* In other words, this method behaves exactly as if it simply
* performs the call {@code wait(0)}.
*
翻译:换句话说,这个方法就是调用了 wait(0) ,也就是默认参数是0
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* <p>
翻译:当线程一定是要获取了该对象的 monitor 才能调用。调用方法后,线程会释放掉
对象的 monitor,然后进入waits状态,一直到其他的线程通过 notify()或者notifyAll() 方法通知
那些等待竞争获取该对象的monitor的线程们,进入唤醒状态。线程会一直等待,直到重新获取到对象
的monitor后,才能继续执行。
* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of the object's monitor.
翻译: 如果当前线程没有获取到 对象的monitor,会抛出 IllegalMonitorStateException异常
* @throws InterruptedException if any thread interrupted the
* current thread before or while the current thread
* was waiting for a notification. The <i>interrupted
* status</i> of the current thread is cleared when
* this exception is thrown.
翻译:当其他线程 打断该线程的时候, 会抛出InterruptedException异常, 当异常抛出的时候,
打断状态就会被清除掉。(这点和 Thread.sleep() 方法抛出的异常一致)
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
public final void wait() throws InterruptedException {
wait(0);
}
二、案例
1.一个案例说明全部问题
代码如下(示例):
package com.kinyang.objectwait;
import java.util.Scanner;
/**
* @author KinYang.Liu
* @date 2021/6/11 2:02 下午
*/
public class ObjectWaitTest {
private static Object monitor = new Object();
public static void main(String[] args) throws InterruptedException {
创建一个 t1 线程
Thread t1 = new Thread(() -> {
synchronized (monitor){
try {
System.out.println("t1 线程调用wait方法,进入休眠");
monitor.wait();
System.out.println("t1 线程,被唤醒了,继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t1-thread");
启动t1线程
t1.start();
Thread.sleep(3000L);
System.out.println("查看一下 t1 线程当前的状态:"+t1.getState());
synchronized (monitor){
System.out.println("main线程 获得了锁,然后调用 notify方法");
monitor.notify();
}
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
if (scanner.nextLine().equalsIgnoreCase("bye")){
System.out.println("再见");
break;
}else {
System.out.println("指令错误");
}
}
}
}
执行结果
从结果我们可以知道,t1线程调用 wait方法后,线程会进入 WAITING状态,然后其他线程调用 notify方法后,t1线程又被唤醒,重新进入执行状态。
所以wait方法一定要结合着notify一起来使用,而且,wait方法一定是放在synchronized代码里面执行,也就是当前线程必须获取到了 对象的monitor,才能执行对象的wait方法。
另外,方法也会抛出一个InterruptedException异常,和Thread.sleeep一样,当休眠的时候,如果被打断,也会抛出异常,并且打断状态也会被清除,具体可以参考上一篇Thread.sleep的InterruptedException案例分析。
如果wait()方法,指定了参数的话,表示的是休眠多久的意思(没有参数或者参数是0表示没有时间限制),在超过时间后,线程自动唤醒,然后去竞争锁,获取了,就继续执行,没有获取就继续等待。如果wait有参数的话,线程进入的是 TIMED_WAITING状态,不是WAITING状态。
从执行顺序上来看,也验证了wait方法会释放锁,否则的话,“main线程 获得了锁,然后调用 notify方法” 这句话就不会打印,notify()方法也不会执行,那么t1线程就会一直等待,就死锁了。所以wait方法一定是要释放到当前线程所持有的锁的。
这样一定也和Thread.sleep()有这本质区别。
总结
总结一下:
Object.wait()和Object.notify()一定是配合使用的,必须放在的synchronized代码块或者方法里,wait方法会导致线程释放锁,进入WAITING状态或者TIMED_WAITING状态(取决于是否设置了时间)。
当进入WAITING状态的时候,只能通过其他线程调用notify()方法唤醒,线程被唤醒后,需要重新竞争锁,获取到锁后,才能执行,如果获取失败,还会是进入WAITING状态。
当进入TIMED_WAITING状态的时候,除了上面说的notify()方法唤醒后,时间到了也会自动唤醒,唤醒后还是重新竞争锁,获取到锁后,才能执行,如果获取失败,还会是进入WAITING状态。
Object.wait()也会抛出InterruptedException异常,这点和Thread.sleep()方法一样。当线程被打断抛出异常后,打断状态也会被清除。