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

如何在ScheduledExecutorService中抛出并向上传递异常?

问题分析

你的代码存在两个核心问题:

  1. scheduleWithFixedDelay会重复执行任务,哪怕第一次已经触发了delayed.completeExceptionally,后续任务依然会每隔150ms尝试再次调用该方法,但CompletableFuture只能进入完成状态一次,后续调用无效,同时任务持续运行导致异常循环。
  2. whenCompleteAsync是异步执行的回调,其中抛出的异常会被底层线程池(默认是ForkJoinPool)捕获并忽略,无法传递到restart方法的调用栈中。
正确处理方案

要实现异常向上传递,需要做到两点:异常发生后立即停止定时任务,以及将异步异常同步到当前线程抛出。

修改后的代码示例:

public void restart() throws Exception {
    CompletableFuture<?> delayed = new CompletableFuture<>();
    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    
    // 保存定时任务的Future,用于取消任务
    ScheduledFuture<?> scheduledFuture = executorService.scheduleWithFixedDelay(() -> {
        try {
            throw new RuntimeException("My exception.");
        } catch (Exception e) {
            // 仅在CompletableFuture未完成时标记异常完成
            if (!delayed.isDone()) {
                delayed.completeExceptionally(e);
            }
            // 异常发生后终止当前任务,避免重复执行
            Thread.currentThread().interrupt();
        }
    }, 1000L, 150L, TimeUnit.MILLISECONDS);

    try {
        // 阻塞当前线程,等待CompletableFuture完成
        delayed.get();
    } catch (ExecutionException e) {
        // 解包底层异常并向上抛出
        throw (Exception) e.getCause();
    } finally {
        // 确保线程池关闭,防止资源泄漏
        executorService.shutdownNow();
    }
}
关键改进点
  • 保存ScheduledFuture实例,异常发生后通过Thread.currentThread().interrupt()终止当前任务,结合executorService.shutdownNow()彻底关闭线程池,避免任务重复运行。
  • 使用delayed.get()阻塞当前线程,等待异步任务的结果/异常,通过ExecutionException解包真实异常后向上抛出,让异常能被restart方法的调用者捕获。
  • 增加!delayed.isDone()判断,避免重复调用completeExceptionally(虽不影响结果,但更严谨)。
另一种简化方案(无需CompletableFuture)

如果不需要异步回调逻辑,直接利用ScheduledFuture的get()方法也能实现异常传递:

public void restart() throws Exception {
    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    
    ScheduledFuture<?> scheduledFuture = executorService.scheduleWithFixedDelay(() -> {
        throw new RuntimeException("My exception.");
    }, 1000L, 150L, TimeUnit.MILLISECONDS);

    try {
        // 等待任务抛出异常
        scheduledFuture.get();
    } catch (ExecutionException e) {
        throw (Exception) e.getCause();
    } finally {
        executorService.shutdownNow();
    }
}

注意:scheduleWithFixedDelay的任务中未捕获的异常会导致任务终止,此时ScheduledFuture.get()会抛出ExecutionException,我们只需要解包并抛出真实异常即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 15:13:27