You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何等待线程调用interrupt()有效,notify()却无法唤醒?

线程notify()唤醒失效原因解析

问题描述

创建了两个示例类(忽略Map的get(1L)访问细节,仅还原真实项目核心数据结构),核心问题为线程唤醒异常:

  • MyThread类:创建匿名线程,该线程在自身监视器(synchronized(this))上调用wait()进入等待;主线程调用该线程的join()等待其结束。
  • Handler类:内部维护存储线程的Map,计数到3时,对目标线程同步后调用t.notify(),但无法唤醒等待线程;替换为t.interrupt()则能成功中断并唤醒线程。

确认使用同一监视器,但notify()失效,寻求原因解析。

MyThread类代码

public class MyThread {

    public static void main(String[] args) {
        MyThread t = new MyThread();
    }

    public MyThread() {
        Thread t = new Thread() {
            @Override
            public void run() {
                synchronized (this) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        System.out.println("interrupted");
                    }
                }
            }
        };

        Handler handler = new Handler(t);

        System.out.println("starting thread");
        t.start();

        try {
            t.join();
            System.out.println("joined! (woke up)");
        } catch (InterruptedException e) {
            System.out.println("MyThread interrupted");
        }

    }
}

Handler类代码

public class Handler {

    NavigableMap<Long, Set<Thread>> map = new TreeMap<>();

    public Handler(Thread t2) {
        System.out.println("in Handler");
        map.put(1L, new HashSet<>());
        map.get(1L).add(t2);

        Thread t = new Thread(new Runnable() {
            int counter = 0;
            @Override
            public void run() {
                try {
                    while (true) {
                        System.out.println("waiting: " + counter);
                        Thread.sleep(3000);
                        if (++counter == 3) {
                            for (Thread t : map.get(1L)) {
                                synchronized (t) {
                                    System.out.println("notifying!");
                                    t.notify();
                                    System.out.println("notified!");
                                }
                            }
                        }
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
    }
}

原因解析

问题核心是Thread类的join()方法会占用线程对象的监视器锁,并在内部调用wait(),和你自己的wait()调用产生了唤醒冲突。

具体执行流程:

  1. 匿名线程启动后,进入run()方法,获取自身监视器锁,调用wait()释放锁并进入等待状态。
  2. 主线程调用t.join(),join()的内部实现会先获取t的监视器锁,然后调用t.wait()(目的是等待线程终止)。
  3. Handler中的计数线程计数到3时,获取t的监视器锁并调用t.notify(),但这个notify()唤醒的是主线程中因join()而进入等待的线程,而非匿名线程里调用wait()的那个等待状态。
  4. 主线程被唤醒后,会检查匿名线程是否已经终止(此时匿名线程还在wait()状态,并未结束),所以主线程会再次调用t.wait(),重新进入等待。而匿名线程的wait()始终没被唤醒,导致程序卡住。

换成t.interrupt()时,会直接触发匿名线程的wait()抛出InterruptedException,让匿名线程退出等待并结束运行,主线程的join()检测到线程终止后自然完成,所以能成功“唤醒”整个流程。

简单总结:你的notify()被主线程的join()“截胡”了,唤醒的是主线程而非目标等待线程。

内容的提问来源于stack exchange,提问作者rzdrrw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 22:30:39