调用notify方法后线程仍处于等待状态的Java多线程问题咨询
问题根因
- WorkerQueue.get()的等待循环逻辑缺陷
当你调用endTask()仅执行了obj.notify(),线程被唤醒后会回到while (mTaskQueue.isEmpty())的判断分支,此时队列仍为空,会直接再次进入obj.wait()状态,永远跳不出get方法,自然也不会走到外层while去判断mFlag的值。而你调用add(...)的时候会往队列塞入元素,唤醒后队列不为空,就能跳出循环返回,之后外层判断mFlag为false就结束线程了。 - mFlag变量无可见性保证
WorkerRunnable里的mFlag没有加volatile修饰,主线程调用endThread()修改的mFlag值可能不会被工作线程实时感知到,存在线程安全隐患。 - 额外代码笔误:
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
相关产品推荐
相关产品推荐

