You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

调用wait()后未触发notify(),线程能否继续执行?相关机制存疑

嘿,咱们来把这两个线程等待相关的问题掰扯清楚——我天天跟Java线程模型打交道,这种疑问太常见了!

问题1:线程调用target.wait()后,未调用target.notify()的情况下,能否继续执行后续指令?

答案是:大部分情况下不能,但存在几个例外场景

正常流程下,线程调用target.wait()时,会先释放持有的target对象锁,然后进入该对象的等待队列,一直阻塞在wait()方法这一行,不会往下执行后续代码。只有当以下几种情况发生时,它才会从wait()中返回,继续执行后续逻辑:

  • 其他线程调用了target.notify()target.notifyAll(),并且这个线程被选中唤醒;
  • 调用的是带超时参数的wait(long timeout)wait(long timeout, int nanos),超时时间到了;
  • 等待的线程被其他线程调用interrupt()方法中断,此时会抛出InterruptedException(如果代码捕获了这个异常,处理完后会继续执行后续代码);
  • 发生虚假唤醒:JVM允许在没有任何notify信号的情况下,让等待队列中的线程醒来,这是底层操作系统的调度特性导致的,虽然概率极低,但规范里明确要求必须处理这种情况。
问题2:关于“线程执行目标对象的wait()方法后进入等待队列,在目标对象的notify()方法被调用前,不会执行任何后续指令”的说法可靠性?

这个说法并不完全可靠,存在不少例外情况,这也是你注释掉notify()后结果不符的原因。咱们来拆解一下可能的问题:

首先,这个说法只在理想场景下成立:线程正确持有target的对象锁(wait()必须在同步块/同步方法内调用)、使用的是无参wait()、线程没有被中断、也没有发生虚假唤醒。但实际开发中,很容易碰到以下打破这个说法的情况:

  1. 错误使用wait(),没持有对象锁:如果你在调用target.wait()前,没有通过synchronized(target)同步块或同步方法获取锁,JVM会直接抛出IllegalMonitorStateException。如果你的代码捕获了这个异常,处理完后就会继续执行后续代码——看起来像是没notify就继续了,但其实线程根本没进入等待状态。
    举个错误示例:

    static Object target = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                // 没有获取target的锁就调用wait()
                target.wait();
                System.out.println("线程继续执行了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    

    这段代码会抛出异常,然后执行println,和你看到的现象一致。

  2. 使用了带超时的wait():如果你的代码用的是wait(1000)这种带超时的方法,哪怕没有notify,时间到了线程也会自动醒来继续执行。

  3. 线程被中断:如果其他线程调用了这个等待线程的interrupt()方法,等待线程会抛出InterruptedException,处理完异常后继续执行后续逻辑。

  4. 虚假唤醒:虽然概率低,但JVM允许这种情况发生,所以规范里要求我们必须把wait()放在while循环中,而不是if判断,用来重新检查等待条件,避免虚假唤醒后直接执行后续代码。

总结一下:那个说法是简化后的理想情况,实际开发中必须考虑上述例外场景,不能把它当成绝对规则。

内容的提问来源于stack exchange,提问作者Kishore Kumar Korada

火山引擎 最新活动