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

为何ExecutorService提交的任务无法异步执行?

问题解决:Java异步任务需等待main执行完毕才启动

问题场景

在IntelliJ中调试Java异步代码时,调用calculateAsync()方法后,提交给ExecutorService的任务并未并发执行,而是要等到main方法执行完毕才开始运行。断点设置在System.out.println("calculate async was called");处,终端输出顺序为:

inside calculate async method
outside executores block
calculate async was called
inside the submitted task
completed the thread

问题根源

每次调用calculateAsync()时,Executors.newFixedThreadPool(10)创建的线程池是局部变量,方法执行完成后该线程池失去引用,会被JVM标记为可回收对象。ThreadPoolExecutor的finalize()方法会自动调用shutdown(),虽然shutdown()不会终止已提交的任务,但在main线程运行期间,JVM的Finalizer线程可能延迟处理线程池的回收逻辑,导致任务被暂缓执行,直到main线程结束后才触发任务调度。

解决方案

方案1:将线程池改为类成员变量(推荐)

保持线程池的引用,避免被垃圾回收,同时复用线程池减少资源开销:

修改CompletableFuturePlay.java:

public class CompletableFuturePlay {
    // 类成员变量持有线程池引用
    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    public void calculateAsync() throws InterruptedException {
        System.out.println("inside calculate async method");
        executor.submit(() -> {
            System.out.println("inside the submitted task");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("completed the thread");
        });

        System.out.println("outside executores block");
    }

    // 程序结束时关闭线程池,避免资源泄漏
    public void shutdown() {
        executor.shutdown();
    }
}

修改PlayGround.java,确保线程池正常关闭:

public class PlayGround {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuturePlay completableFuturePlay = new CompletableFuturePlay();
        try {
            completableFuturePlay.calculateAsync();
            System.out.println("calculate async was called");
            // 若需观察异步任务执行,可添加短暂等待(调试时可去掉)
            // Thread.sleep(2000);
        } finally {
            completableFuturePlay.shutdown();
        }
    }
}

方案2:用CompletableFuture包装任务(快速验证)

使用CompletableFuture.runAsync()包装任务,它会持有任务和线程池的引用,避免提前被回收:

public void calculateAsync() throws InterruptedException {
    System.out.println("inside calculate async method");
    CompletableFuture.runAsync(() -> {
        System.out.println("inside the submitted task");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("completed the thread");
    }, Executors.newFixedThreadPool(10));

    System.out.println("outside executores block");
}

调试验证

修改后,在System.out.println("calculate async was called");处设置断点,main线程暂停时,异步任务会立即执行,终端会提前输出inside the submitted task,符合并发预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:43:13