多线程编排工具 CompletableFuture

166 阅读5分钟

一、CompletableFuture 出现的意义

在日常开发中,接口中可能出现很多可以并行的耗时操作【一般指计算任务或网络调用任务】,针对这种耗时操作我们一般都会使用线程池去将多个任务并行处理以减少接口RT,但是线程池只是针对于将单个任务开启异步,没有编排功能,对于多任务顺序执行等要求没有便捷的方法处理。所以CompletableFuture应运而生,CompletableFuture用于并行任务编排,提供诸如并行任务、顺序任务、超时处理等便捷方法,使开发复杂逻辑并行任务的工作量降低。
CompletableFuture内部实现比较复杂,所以写起来需要开发人员对其api有较为深入的理解,这个也可能是它的一个小缺点。但对于它提供的方法来说,这点小瑕咝,瑕不掩瑜。

二、方法使用介绍样例

本届的样例使用的全部是默认的线程池。【(ForkJoinPool.getCommonPoolParallelism() > 1) ?ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();】

1、多任务执行并合并

public static void test1() throws ExecutionException, InterruptedException {

        /*
			功能:通过不同接口获取国内天气信息,只需要一个返回完成即可。
					 使用国内的天气信息与国外天气信息对比,查看差异
        */
        CompletableFuture<Integer> stage = CompletableFuture.supplyAsync( // c1
                () -> {
                    stopTheWorld(2);
                    printer("从北京气象台获取全国天气信息");
                    return 1;
                }
            )
            // 开启任务c2, c1,c2其中一个完成则使用最先完成的任务结果执行function
            .applyToEither(CompletableFuture.supplyAsync( // c2
                () -> {
                    stopTheWorld(3);
                    printer("从上海气象台获取全国天气信息");
                    return 2;
                }
            ), in -> { // function
                Integer out = in + 10;
                printer("fun in[" + in + "],out[" + out + "]");
                return out;
            })
            /*  将上述任务结果与任务c3结果组合 */
            .thenCombine(CompletableFuture.supplyAsync( // c3
                () -> {
                    stopTheWorld(5);
                    printer("获取俄罗斯天气信息");
                    return 3;
                }
            ), (r1, r2) -> { // combine
                printer("combine ->" + (r1 + r2));
                return r1 + r2;
            });

            ;

        printer("stage.get = " + String.valueOf(stage.get()));
        stopTheWorld();
    }

/*
	结果:
	ForkJoinPool.commonPool-worker-1	 | 	从北京气象台获取全国天气信息	 | 	1694508851653
	ForkJoinPool.commonPool-worker-1	 | 	fun in[1],out[11]	 | 	1694508851660
	ForkJoinPool.commonPool-worker-2	 | 	从上海气象台获取全国天气信息	 | 	1694508852655
	ForkJoinPool.commonPool-worker-3	 | 	获取俄罗斯天气信息	 | 	1694508854647
	ForkJoinPool.commonPool-worker-3	 | 	combine ->14	 | 	1694508854650
	main	 | 	stage.get = 14	 | 	1694508854650
*/

2、顺序执行处理异步任务

类似于Stream.map,Optional.map

public static void test2() throws ExecutionException, InterruptedException {

        /*
            按照顺序执行任务
         */
        CompletableFuture<Void> future = CompletableFuture.runAsync(
                () -> {
                    stopTheWorld(3);
                    printer("调用北京气象台接口");
                }
            )
            // 接收上一步返回的值,根据场景将其拼装成另外一个格式或对象返回
            .thenApply(in -> {
                stopTheWorld(2);
                printer("thenApply1 -> " + in);
                return 1;
            })
            .thenApply(in -> {
                stopTheWorld(2);
                printer("thenApply2 -> " + in);
                return ++in;
            })
            .thenAccept(in -> {
                printer("thenAccept -> " + in);
            });

        printer("future = " + future.get());

        stopTheWorld();
    }
/*
ForkJoinPool.commonPool-worker-1	 | 	调用北京气象台接口	 | 	1694509364504
ForkJoinPool.commonPool-worker-1	 | 	thenApply1 -> null	 | 	1694509366513
ForkJoinPool.commonPool-worker-1	 | 	thenApply2 -> 1	 | 	1694509368526
ForkJoinPool.commonPool-worker-1	 | 	thenAccept -> 2	 | 	1694509368526
main	 | 	future = null	 | 	1694509368526
*/

3、任务超时处理-使用默认值

public static void test3() {

		/**
		*  调用接口超时则返回默认值
		*/
        CompletableFuture.supplyAsync(
            () -> {
                stopTheWorld(new Random().nextInt(4));
                printer("调用北京气象台接口");
                return 1;
            }
        )
            // 设置超时,如果 supplyAsync执行超时则返回默认数据
            .completeOnTimeout(-1, 3, TimeUnit.SECONDS)
            .thenAccept(in -> {
                printer("thenAccept -> " + in);
               if(in > 0) {
                   printer("调用接口成功");
               } else {
                   printer("调用接口超时,使用默认数据");
               }
            });

        stopTheWorld();
    }
/*
ForkJoinPool.commonPool-worker-1	 | 	调用北京气象台接口	 | 	1694509634116
CompletableFutureDelayScheduler	 | 	thenAccept -> -1	 | 	1694509634118
CompletableFutureDelayScheduler	 | 	调用接口超时,使用默认数据	 | 	1694509634118


ForkJoinPool.commonPool-worker-1	 | 	调用北京气象台接口	 | 	1694509720762
ForkJoinPool.commonPool-worker-1	 | 	thenAccept -> 1	 | 	1694509720764
ForkJoinPool.commonPool-worker-1	 | 	调用接口成功	 | 	1694509720764
*/

4、多任务执行-全部完成后执行后续

public static void test4() {

		/*
			多任务处理
		*/
        CompletableFuture.allOf(
            CompletableFuture.supplyAsync(
                () -> {
                    stopTheWorld(2);
                    printer("task1");
                    if (true) {
                        throw new RuntimeException("error1.");
                    }
                    return 1;
                }
            )
                // 这里捕获了异常后则不会传递到外层
                .exceptionallyAsync(ex -> {
                    printer("task1 ex -> " + ex.getMessage());
                    return -1;
                }),
            CompletableFuture.supplyAsync(
                () -> {
                    stopTheWorld(1);
                    printer("task2");
                    if (true) {
                        throw new RuntimeException("error2.");
                    }
                    return 2;
                }
            ),
            CompletableFuture.supplyAsync(
                () -> {
                    printer("task3");
                    if (true) {
                        throw new RuntimeException("error.");
                    }
                    return 3;
                }
            )
        )
            // 这里相当于一个统一的异常处理,ex=最后一个任务的异常信息
            .exceptionallyAsync(ex -> {
                printer("outer ex -> " + ex.getMessage());
                return null;
            })
            .thenAcceptAsync(ignore -> {
                printer("thenAcceptAsync");
            })
        ;

        stopTheWorld();
    }
/*
ForkJoinPool.commonPool-worker-3	 | 	task3	 | 	1694509884066
ForkJoinPool.commonPool-worker-2	 | 	task2	 | 	1694509885077
ForkJoinPool.commonPool-worker-1	 | 	task1	 | 	1694509886076
ForkJoinPool.commonPool-worker-2	 | 	task1 ex -> java.lang.RuntimeException: error1.	 | 	1694509886078
ForkJoinPool.commonPool-worker-2	 | 	outer ex -> java.lang.RuntimeException: error2.	 | 	1694509886079
ForkJoinPool.commonPool-worker-2	 | 	thenAcceptAsync	 | 	1694509886079
*/

5、任务执行-异常处理handle

public static void test6() {

        CompletableFuture.supplyAsync(
            () -> {
                stopTheWorld(1);
                printer("supplyAsync");
                return 1;
            }
        )
            // handleAsync 值和异常一起处理
            .handleAsync(
            (in, ex) -> {
                stopTheWorld(2);
                printer("handleAsync1 -> " + in);
                if(ex != null) {
                    printer("handleAsync1 catch ex -> " + ex.getMessage());
                }
                return in;
            }
        ).thenApplyAsync(in -> {
            stopTheWorld(3);
            printer("thenApplyAsync -> " + in);
            if (true) {
                throw new RuntimeException("error.");
            }
            return in;
        }).handleAsync(
            (in, ex) -> {
                stopTheWorld(4);
                printer("handleAsync2 -> " + in);
                if(ex != null) {
                    printer("handleAsync2 catch ex -> " + ex.getMessage());
                }
                return in;
            }
        );

        stopTheWorld();
    }
/*
ForkJoinPool.commonPool-worker-1	 | 	supplyAsync	 | 	1694510132633
ForkJoinPool.commonPool-worker-1	 | 	handleAsync1 -> 1	 | 	1694510134643
ForkJoinPool.commonPool-worker-1	 | 	thenApplyAsync -> 1	 | 	1694510137652
ForkJoinPool.commonPool-worker-1	 | 	handleAsync2 -> null	 | 	1694510141659
ForkJoinPool.commonPool-worker-1	 | 	handleAsync2 catch ex -> java.lang.RuntimeException: error.	 | 	1694510141659
*/

6、多任务执行-任务超时机制

public static void test7() {

        CompletableFuture.supplyAsync(
            () -> {
                stopTheWorld(5);
                printer("task1");
                return 1;
            }
        )
            .orTimeout(3, TimeUnit.SECONDS)
            // 3s 未完成任务则抛出异常
            // 两个任务全部完成后执行runnable
            .runAfterBothAsync(CompletableFuture.supplyAsync(
                () -> {
                    stopTheWorld(3);
                    printer("task2");
                    return 2;
                }
            ), () -> {
                printer("runnable");
            })
            // 完成上面任务后执行该步骤
            .whenCompleteAsync((in, ex) -> {
                printer("whenCompleteAsync -> " + in);
                if(ex != null) {
                    printer("whenCompleteAsync ex -> " + ex.getMessage());
                }
            });

        stopTheWorld();
    }
/*
ForkJoinPool.commonPool-worker-2	 | 	task2	 | 	1694511076504
ForkJoinPool.commonPool-worker-2	 | 	whenCompleteAsync -> null	 | 	1694511076507
ForkJoinPool.commonPool-worker-2	 | 	whenCompleteAsync ex -> java.util.concurrent.TimeoutException	 | 	1694511076507
ForkJoinPool.commonPool-worker-1	 | 	task1	 | 	1694511078495
*/

三、结束语

文章到此结束,如果有问题者可以留言交流哦。