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

调用notify方法后线程仍处于等待状态的Java多线程问题咨询

问题根因

  1. WorkerQueue.get()的等待循环逻辑缺陷
    当你调用endTask()仅执行了obj.notify(),线程被唤醒后会回到while (mTaskQueue.isEmpty())的判断分支,此时队列仍为空,会直接再次进入obj.wait()状态,永远跳不出get方法,自然也不会走到外层while去判断mFlag的值。而你调用add(...)的时候会往队列塞入元素,唤醒后队列不为空,就能跳出循环返回,之后外层判断mFlag为false就结束线程了。
  2. mFlag变量无可见性保证
    WorkerRunnable里的mFlag没有加volatile修饰,主线程调用endThread()修改的mFlag值可能不会被工作线程实时感知到,存在线程安全隐患。
  3. 额外代码笔误:WorkerRunnable构造方法里引用了未定义的printerManager、mPrinterManager变量,实际运行会编译报错。

修复方案

第一步:修改WorkerQueue,增加停止标识

在WorkerQueue中新增停止标记,get()方法同时判断队列状态和停止状态,endTask()时先设置标记再唤醒等待线程:

class WorkerQueue {
    private Queue<BasePrintTask> mTaskQueue = new ArrayDeque<>();
    private final Object obj = new Object();
    // 新增停止标记,volatile保证多线程可见性
    private volatile boolean isStop = false;

    public BasePrintTask get() {
        synchronized (obj) {
            // 同时判断队列空和未停止状态
            while (mTaskQueue.isEmpty() && !isStop) {
                try {
                    obj.wait();
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                    // 被中断视为停止信号
                    isStop = true;
                }
            }
            // 停止状态且队列空返回null,否则返回队列元素
            if (isStop && mTaskQueue.isEmpty()) {
                return null;
            }
            return mTaskQueue.remove();
        }
    }

    public void add(BasePrintTask task) {
        synchronized (obj) {
            if (!isStop) {
                mTaskQueue.add(task);
                obj.notify();
            }
        }
    }

    public void endTask() {
        synchronized (obj) {
            isStop = true;
            // 用notifyAll更稳妥,避免多消费者场景漏唤醒
            obj.notifyAll();
        }
    }
}

第二步:修改WorkerRunnable,补全可见性和停止逻辑

public class WorkerRunnable implements Runnable {
    // 加volatile保证mFlag的多线程可见性
    private volatile boolean mFlag = false;
    private WorkerQueue mWorkerQueue = null;

    // 移除未定义的printerManager相关代码
    public WorkerRunnable(WorkerQueue workerQueue) {
        this.mWorkerQueue = workerQueue;
    }

    public void endThread() { mFlag = false; }

    public void startThread() { mFlag = true; }

    @Override
    public void run() {
        while(mFlag) {
            try {
                BasePrintTask task = mWorkerQueue.get();
                // get返回null说明队列已停止,直接跳出循环
                if (task == null) {
                    break;
                }
                // 此处补你原有任务处理逻辑
                // task.run();
            } catch (NoSuchElementException el) {
                // 队列已清空且停止,直接退出循环
                break;
            }
        }
    }
}

修改后原有的destroy调用逻辑无需改动,即可正常终止工作线程。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 15:06:02