切换语言为:繁体

Java中CompletableFuture allOf调用的时候如果出现异常会怎么样

  • 爱糖宝
  • 2024-10-30
  • 2042
  • 0
  • 0

在日常开发中如果使用 CompletableFuture 的时候,如果出现异常,会怎么样呢,会只停止这个线程还是全部停止?会主动抛出异常吗,今天我通过一下案例来以结果来分析。

开发环境

# java 环境
openjdk 23.0.1 2024-10-15
OpenJDK Runtime Environment (build 23.0.1+11-39)
OpenJDK 64-Bit Server VM (build 23.0.1+11-39, mixed mode, sharing)

两个 CompletableFuture 一个抛异常 一个不抛异常,执行结果是什么呢

/**
 * 不抛异常 
 */
public CompletableFuture<Void> normalCompletableFuture() {
    return CompletableFuture.runAsync(() -> {
        // 这里让线程休眠10秒
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            log.info("睡眠过程中出现异常 -> {}", e);
        } finally {
            log.info("普通线程执行完毕");
        }
    });
}

/**
 * 抛异常的线程
 */
public CompletableFuture<Void> exceptionCompletableFuture() {
    return CompletableFuture.runAsync(() -> {
        throw new RuntimeException("runAsync -> 发生了一个大的异常");
    });
}

然后可以使用 allOf 来组合两个线程

public void testRunRunAsync() {
    CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(this.exceptionCompletableFuture(), this.normalCompletableFuture());
    voidCompletableFuture.join();
}

执行以后得到以下的执行结果

09:22:56.319 [ForkJoinPool.commonPool-worker-1] INFO org.flow.basic.threadpool.CompletableFutureRuntimeDemo -- 普通线程执行完毕
java.lang.RuntimeException: runAsync -> 发生了一个大的异常
    at org.flow.basic.threadpool.CompletableFutureRuntimeDemo.lambda$exceptionCompletableFuture$1(CompletableFutureRuntimeDemo.java:38)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1848)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1840)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:507)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1458)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:2034)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:189)

我们可以看到虽然抛出异常了,但是并不会因为异常而让全部的执行的任务都停止,他会等待所有的任务都执行完毕,然后抛出异常。那如果不实用 join 使用其他的 api 是否会抛出异常呢? 把执行的代码略作改动我们再来执行看看效果

public void testRunRunAsync() {
    CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(this.exceptionCompletableFuture(), this.normalCompletableFuture());

    while (true) {
        if (voidCompletableFuture.isDone()) {
            log.info("线程执行完毕");
            break;
        }
    }
}

执行结果

09:36:27.601 [ForkJoinPool.commonPool-worker-1] INFO org.flow.basic.threadpool.CompletableFutureRuntimeDemo -- 普通线程执行完毕
09:36:27.604 [main] INFO org.flow.basic.threadpool.CompletableFutureRuntimeDemo -- 线程执行完毕

这个可以看出我们并没有使用阻塞的方式,而是使用了 while 的死循环,这个会对 cpu 带来压力,不过我么可以看到他并不会报错并且两个任务全部执行完毕。

两个 CompletableFuture 一个抛异常 一个不抛异常,如果执行呢?

public void testRunRunAsync() {
    CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(this.exceptionCompletableFuture(), this.normalCompletableFuture());
    voidCompletableFuture.exceptionally(e -> {
        log.error("发生异常,主动处理 -> {}", e.getMessage());
        return null;
    }).join();
    log.info("正常执行完毕");
}

打印结果

09:51:36.359 [ForkJoinPool.commonPool-worker-1] INFO org.flow.basic.threadpool.CompletableFutureRuntimeDemo -- 普通线程执行完毕
09:51:36.362 [ForkJoinPool.commonPool-worker-1] ERROR org.flow.basic.threadpool.CompletableFutureRuntimeDemo -- 发生异常,主动处理 -> java.lang.RuntimeException: runAsync -> 发生了一个大的异常
09:51:36.363 [main] INFO org.flow.basic.threadpool.CompletableFutureRuntimeDemo -- 正常执行完毕

这个我们可以看出如果主动处理了异常,但是没有使用 join 或者 isDone 等方式来判断,那么就会导致程序阻塞,并且不会抛出异常。

结论

CompletableFuture 的 allOf 的时候如果出现异常,不会主动抛出异常,但是会等待所有的任务都执行完毕,然后抛出异常。如果不用 join 等待,也可以通过 isDone 来判断是否执行完毕,这个并不会主动抛出异常,这个要自己来判断异常。但是会对 CPU 带来负载,这个时候可以根据实际情况来做衡量取舍。但是如果使用了 exceptionally 来处理异常,那么就会被异常处理拦截会根据自己的处理来做实际的异常行为

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.