异步编程四:CompletableFuture计算完成回调

60 阅读2分钟

当我们想第一个异步任务执行完成后,还需要做其他的事情。我们的CompletableFuture提供了计算完成时回调方法,whenCompletewhenCompleteAsyncexceptionally等接口。

public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)

whenComplete 可以处理正常和异常的计算结果,exceptionally: 处理异常情况。 whenCompletewhenCompleteAsync 的区别是whenComplete 是执行当前任务的线程继续执行whenComplete的任务。 whenCompleteAsync: 是把whenCompleteAsync的任务继续提交给线程池来进行执行。

whenCompleteAsync

public class CompletableFutureWhenCompleteAsync {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);

        System.out.println("main start ...");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("开启异步任务...");
            int i = 10 / 2;
            return i;
        }, executor).whenCompleteAsync((res, exc) -> {
            System.out.println("异步任务完成了,执行结果是:" + res + "  异常是:" + exc);
        });
        System.out.println("获取异步任务返回值:" + future.get());
        System.out.println("main end ...");

        executor.shutdown();
    }
}

执行结果:

main start ...
开启异步任务...
异步任务完成了,执行结果是:5  异常是:null
获取异步任务返回值:5
main end ...

如果异步任务出现了异常,可以通过exc打印异常,我们在程序中设置一个运行时异常 ,如下:

public class CompletableFutureWhenCompleteAsync1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);

        System.out.println("main start ...");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("开启异步任务...");
            int i = 10 / 0;
            return i;
        }, executor).whenCompleteAsync((res, exc) -> {
            System.out.println("异步任务完成了,执行结果是:" + res + "  异常是:" + exc);
        });
        System.out.println("获取异步任务返回值:" + future.get());
        System.out.println("main end ...");

        executor.shutdown();
    }
}

执行结果:

main start ...
开启异步任务...
异步任务完成了,执行结果是:null  异常是:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
	at com.qjw.thread.CompletableFutureWhenCompleteAsync1.main(CompletableFutureWhenCompleteAsync1.java:24)
Caused by: java.lang.ArithmeticException: / by zero
	at com.qjw.thread.CompletableFutureWhenCompleteAsync1.lambda$main$0(CompletableFutureWhenCompleteAsync1.java:19)
	at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1604)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)

exceptionally

whenComplete虽然可以得到异常信息,但是无法修改结果,exceptionally可以感知异常,同时可以返回默认值。

public class CompletableFutureWhenExceptionally {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);

        System.out.println("main start ...");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("开启异步任务...");
            int i = 10 / 0;
            return i;
        }, executor).whenCompleteAsync((res, exc) -> {
            System.out.println("异步任务完成了,执行结果是:" + res + "  异常是:" + exc);
        }).exceptionally(throwable -> {
            System.out.println("进入了异常处理,捕获了" + throwable.getMessage() + "异常");
            return 5;
        });
        System.out.println("获取异步任务返回值:" + future.get());
        System.out.println("main end ...");

        executor.shutdown();
    }
}

执行结果:

main start ...
开启异步任务...
异步任务完成了,执行结果是:null  异常是:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
进入了异常处理,捕获了java.lang.ArithmeticException: / by zero异常
获取异步任务返回值:5
main end ...

我们可以看到,通过exceptionally可以捕获异步任务抛出来的异常信息,并对异常进行处理,并可以将处理结果返回。