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

不使用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;
}

问题根源分析

  1. 锁使用错误:调用executorLock.wait()前未持有该锁的同步块,会触发IllegalMonitorStateException,虽然被捕获,但wait()不会生效,线程会无意义阻塞;且没有对应的notify()唤醒逻辑,一旦进入等待就无法退出。
  2. 方法静态化错误:isExecutorsAvailable是静态方法,但executorLock应为实例级锁,静态方法访问实例变量会导致逻辑混乱。
  3. execute方法逻辑错误:
    • 客户端线程调用execute后,会通过wait_for_completion(t)阻塞等待任务完成,无法继续提交后续任务。
    • 每次任务完成后创建新的Executor对象,完全违背线程池复用线程的设计初衷,且终止的线程无法再次start()。
  4. 循环条件错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:55:56