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

Java Servlet(Jetty)中Object的wait/notify无法唤醒线程问题

排查Jetty Servlet中wait/notify线程无法唤醒的问题

遇到这种线程一直醒不来的情况,大概率是wait/notify的使用姿势不对,结合Servlet的运行环境,我给你列几个最常见的原因,你可以逐一排查:

  • 锁对象不是同一个实例
    如果你在doPost方法里把lock声明成了局部变量,那发起异步请求的回调线程拿到的lock,和当前调用wait的线程持有的lock根本不是同一个对象!notify只能唤醒等待在同一个锁对象上的线程,这种情况下自然唤不醒。解决办法是让锁对象能被回调线程访问到——比如每个请求创建一个独立的lock(用final修饰,让匿名内部类能捕获),或者如果是全局回调的话,要确保锁对象是全局可见的(但不推荐全局锁,会导致请求间互相干扰)。

  • wait/notify没有在synchronized块内调用
    这是wait/notify的核心规则:调用wait()前必须先持有该锁对象的监视器(也就是进入synchronized(lock)块),调用notify()同理。如果你的代码里没加这个同步块,要么会直接抛出IllegalMonitorStateException,要么线程根本没进入合法的等待队列,后续的notify也找不到它。一定要检查两边的代码:当前线程的wait、回调线程的notify,都要包裹在同一个lock的synchronized块里。

  • notify调用时机过早
    要是回调线程在当前线程调用wait()之前就执行了notify(),那当前线程再进入wait状态后,就没人来唤醒它了。这种情况很容易发生在异步请求响应太快的场景里。解决办法是加一个状态标志,比如用AtomicBoolean记录“是否收到响应”,当前线程在wait前先检查这个标志,如果还没收到再wait;回调线程收到响应后先更新标志,再调用notify。而且要用while循环检查状态,而不是if——因为Java里存在“虚假唤醒”的情况,线程可能会无缘无故被唤醒,这时候必须再次确认状态是否满足。

  • Servlet单例导致的锁冲突
    默认情况下Servlet是单例的,如果你的lock是Servlet的成员变量,那多个请求会共用同一个锁。比如请求A先进入wait,这时候请求B的回调调用了notify,唤醒的可能是请求B的线程(如果它也在wait),而请求A就一直等着。这种场景下,一定要给每个请求分配独立的锁对象,比如在doPost里创建局部的lock,确保每个请求的wait/notify互不干扰。

  • 忽略了InterruptedException
    如果wait过程中线程被中断(比如Jetty超时关闭请求线程),会抛出InterruptedException,要是你没捕获这个异常,线程可能直接退出,不会继续等待。所以在wait周围一定要加try-catch,并且记得恢复线程的中断状态(比如Thread.currentThread().interrupt()),避免后续逻辑出错。

给你贴一个符合规范的示例代码,你可以对比下自己的写法:

private final AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 每个请求独立的锁和状态标记
    final Object lock = new Object();
    final AtomicBoolean isResponseReady = new AtomicBoolean(false);
    final StringBuffer responseContent = new StringBuffer();

    // 发起异步请求到第三方服务
    asyncHttpClient.preparePost("http://your-other-service/endpoint")
            .setBody(req.getInputStream())
            .execute(new AsyncCompletionHandler<Response>() {
                @Override
                public Response onCompleted(Response response) throws Exception {
                    synchronized (lock) {
                        responseContent.append(response.getResponseBody());
                        isResponseReady.set(true);
                        lock.notify(); // 必须在同步块内调用notify
                    }
                    return response;
                }

                @Override
                public void onThrowable(Throwable t) {
                    synchronized (lock) {
                        responseContent.append("Request failed: ").append(t.getMessage());
                        isResponseReady.set(true);
                        lock.notify();
                    }
                }
            });

    // 等待第三方服务响应
    synchronized (lock) {
        while (!isResponseReady.get()) { // 用while循环处理虚假唤醒
            try {
                lock.wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ServletException("Wait interrupted while waiting for response", e);
            }
        }
    }

    // 向客户端返回结果
    resp.setContentType("application/json");
    resp.getWriter().write(responseContent.toString());
}

这个示例里的关键点:每个请求用独立的锁和状态,wait用while循环检查,notify在同步块内,同时处理了异常情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:16:24