不使用ExecutorService实现线程池遇无限等待问题求助
手动实现线程池陷入无限等待问题排查
问题背景
学习线程池原理时,尝试不依赖ExecutorService手动实现线程池,但程序在执行首个任务后进入无限等待状态,无法继续处理后续任务。以下是核心实现代码及问题分析:
核心代码片段
1. 工作线程初始化逻辑
private void create_workers(int size) { for (int i = 0; i < size; i++){ executors[i] = new Executor("Executor :: " + i); availableQueue.offer(executors[i]); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }
2. 客户端调用方式
Pool threadPool = new Pool(5); for (int i = 0; i < 10; i++){ threadPool.execute(new Job("Job : " + i)); }
3. 任务提交方法execute
public void execute(Job job){ LOGGER.log(Level.INFO, "Job added to the queue :: "); jobQueue.offer(job); while(Pool.isExecutorsAvailable()){ Executor t = getExecutor(); if (t.getState().name().equals("NEW")){ t.start(); } wait_for_completion(t); availableQueue.offer(new Executor(t.name)); } }
4. 导致等待的isExecutorsAvailable方法
public static boolean isExecutorsAvailable(){ if(jobQueue.isEmpty()) { try { LOGGER.log(Level.INFO, Thread.currentThread().getName() + " is waiting.."); executorLock.wait(); } catch (IllegalMonitorStateException e) { } catch (InterruptedException e) {} } // executorLock.notify(); return true; }
问题根源分析
- 锁使用错误:调用
executorLock.wait()前未持有该锁的同步块,会触发IllegalMonitorStateException,虽然被捕获,但wait()不会生效,线程会无意义阻塞;且没有对应的notify()唤醒逻辑,一旦进入等待就无法退出。 - 方法静态化错误:
isExecutorsAvailable是静态方法,但executorLock应为实例级锁,静态方法访问实例变量会导致逻辑混乱。 - execute方法逻辑错误:
- 客户端线程调用
execute后,会通过wait_for_completion(t)阻塞等待任务完成,无法继续提交后续任务。 - 每次任务完成后创建新的
Executor对象,完全违背线程池复用线程的设计初衷,且终止的线程无法再次start()。
- 客户端线程调用
- 循环条件错误:
while(Pool.isExecutorsAvailable())的返回值永远为true,加上内部阻塞逻辑,直接导致无限循环等待。
修复方案与线程复用建议
1. 重构工作线程实现(核心:线程复用)
让工作线程启动后循环监听任务队列,有任务则执行,执行完继续等待下一个任务,避免线程终止:
private class Executor extends Thread { public Executor(String name) { super(name); } @Override public void run() { // 线程未中断时持续监听任务队列 while (!Thread.currentThread().isInterrupted()) { try { // 阻塞等待任务,避免空轮询浪费资源 Job job = jobQueue.take(); LOGGER.log(Level.INFO, this.getName() + " executing job: " + job.getName()); job.run(); } catch (InterruptedException e) { // 捕获中断信号,优雅终止线程 Thread.currentThread().interrupt(); LOGGER.log(Level.INFO, this.getName() + " was interrupted, exiting."); break; } } } }
2. 修改工作线程初始化逻辑
直接启动工作线程,让它们进入等待任务的状态,无需放入可用队列:
private void create_workers(int size) { executors = new Executor[size]; for (int i = 0; i < size; i++) { executors[i] = new Executor("Executor :: " + i); executors[i].start(); // 启动后线程自动等待任务 } }
3. 简化任务提交方法execute
客户端仅负责提交任务到队列,无需等待任务执行,也不需要管理工作线程:
public void execute(Job job) { LOGGER.log(Level.INFO, "Job added to the queue :: " + job.getName()); try { jobQueue.put(job); // 若队列满可阻塞等待,更符合线程池特性 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }
4. 移除错误的isExecutorsAvailable方法
原方法逻辑完全不符合线程池设计,直接删除即可,工作线程通过jobQueue.take()自动阻塞等待任务。
关键说明
- 线程一旦进入
TERMINATED状态,绝对不能再次调用start(),这是线程的生命周期规则,线程复用的核心是让线程持续处于RUNNABLE状态,循环处理任务。 - 使用
BlockingQueue的take()方法可以自动实现线程的阻塞等待,无需手动使用wait()/notify(),简化并发逻辑。
内容的提问来源于stack exchange,提问作者Vinod Krishnan
相关产品推荐
相关产品推荐

