java线程之CompletableFuture

CompletableFuture介绍

在线jdk中文文档 https://www.matools.com/api/java8

CompletableFuture 位于 java.util.concurrent包下面

  • 在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
  • 它可能代表一个明确完成的Future,也有可能代表一个完成阶段( CompletionStage ),它支持在计算完成以后触发一些函数或执行某些动作。
  • 它实现了Future和CompletionStage接口

CompletableFuture方法介绍

 

 静态方法

Modifier and TypeMethod and Description
static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
返回一个新的CompletableFuture,当所有给定的CompletableFutures完成时,完成。
static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
返回一个新的CompletableFuture,当任何一个给定的CompletableFutures完成时,完成相同的结果。
static <U> CompletableFuture<U> completedFuture(U value)
返回已经使用给定值完成的新的CompletableFuture。
static CompletableFuture<Void> runAsync(Runnable runnable)
返回一个新的CompletableFuture,它在运行给定操作后由运行在 ForkJoinPool.commonPool()中的任务 异步完成。
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
返回一个新的CompletableFuture,它在运行给定操作之后由在给定执行程序中运行的任务异步完成。
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
返回一个新的CompletableFuture,它通过在 ForkJoinPool.commonPool()中运行的任务与通过调用给定的供应商获得的值 异步完成。
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
返回一个新的CompletableFuture,由给定执行器中运行的任务异步完成,并通过调用给定的供应商获得的值。

以Async结尾并且没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。

 

runAsync方法:它以Runnabel函数式接口类型为参数,所以CompletableFuture的计算结果为空。

supplyAsync方法以Supplier<U>函数式接口类型为参数,CompletableFuture的计算结果类型为U。

 

runAsync(Runnable runnable)与supplyAsync(Supplier<U> supplier)

这两个功能一样,只是一个没有返回值,一个有返回值,示例代码如下:

 

 这两个方法的重构方法不在说明。

completedFuture 

static <U> CompletableFuture<U>    completedFuture(U value)
返回已经使用给定值完成的新的CompletableFuture。

如api介绍,既传入什么值就返回什么值。代码示例:

 

anyOf与allOf

这两个方法参数可以传入多个CompletableFuture,如方法名字所示一个为全部执行完(allOf),一个为任意一个执行结束(anyOf)

如下分别演示allOf、anyOf

public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println(LocalDateTime.now());
        CompletableFuture cf =
                CompletableFuture.allOf(CompletableFuture.supplyAsync(()->{
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("执行完成"+Thread.currentThread().getName());
                    return 123;
                }),CompletableFuture.runAsync(()->{
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(LocalDateTime.now());
                    }
                    System.out.println("执行完成"+Thread.currentThread().getName());
                }));
        System.out.println(cf.get());
    }

 

 可以看到打印信息,执行完成ForkJoinPool.commonPool-worker-1后 执行完成ForkJoinPool.commonPool-worker-2仍然在执行。

如果把allOf改为anyOf后执行如下:

 

 可以看到只要有一个完成,那么整个都就会停止。

 

Modifier and TypeMethod and Description
CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
返回一个新的CompletionStage,当这个或另一个给定阶段正常完成时,执行相应的结果作为提供的操作的参数。
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
返回一个新的CompletionStage,当这个或另一个给定阶段正常完成时,将使用此阶段的默认异步执行工具执行,其中相应的结果作为提供的操作的参数。
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)
返回一个新的CompletionStage,当这个或另一个给定阶段正常完成时,将使用提供的执行器执行,其中相应的结果作为参数提供给函数。

 acceptEither

  • 两个中那个先执行完,则使用最先完成的进行action操作
  • 其中一个执行完毕然后执行action操作,如果主线程没有关闭则另一个线程会继续执行。
  • 如果执行cf.get()则会造成主线程阻塞。等待cf执行完毕。如果不执行则主线程与其他几个线程一起执行

 

public static void acceptEitherOrAsync() throws ExecutionException, InterruptedException {
        // 1、两个中那个先执行完,则使用最先完成的进行action操作
        // 2、其中一个执行完毕然后执行action操作,如果主线程没有关闭则另一个线程会继续执行。
        // 3、如果执行cf.get()则会造成主线程阻塞。等待cf执行完毕。如果不执行则主线程与其他几个线程一起执行
        CompletableFuture cf = CompletableFuture.supplyAsync(()->{
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName()+" "+LocalDateTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return 1;
        }).acceptEither(CompletableFuture.supplyAsync(()->{
//        }).acceptEitherAsync(CompletableFuture.supplyAsync(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" "+LocalDateTime.now());
            }
            return 2;
        }),(x)->{
            System.out.println(x);
        });
        System.out.println(cf.get());
    }

测试代码main:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println(LocalDateTime.now());
        acceptEitherOrAsync();
        for (int i = 0; i < 7; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" 主线程:"+LocalDateTime.now());
        }
        System.out.println(Thread.currentThread().getName()+" 主线程结束");
    }

打印结果如下,可以看到代码是顺序执行:

2021-01-29T11:10:03.053
ForkJoinPool.commonPool-worker-1 2021-01-29T11:10:04.078
ForkJoinPool.commonPool-worker-2 2021-01-29T11:10:04.078
ForkJoinPool.commonPool-worker-2 2021-01-29T11:10:05.079
ForkJoinPool.commonPool-worker-1 2021-01-29T11:10:05.079
ForkJoinPool.commonPool-worker-1 2021-01-29T11:10:06.084
ForkJoinPool.commonPool-worker-2 2021-01-29T11:10:06.084
1
null
main 主线程:2021-01-29T11:10:07.085
ForkJoinPool.commonPool-worker-2 2021-01-29T11:10:07.085
ForkJoinPool.commonPool-worker-2 2021-01-29T11:10:08.085
main 主线程:2021-01-29T11:10:08.085
main 主线程:2021-01-29T11:10:09.087
main 主线程:2021-01-29T11:10:10.087
main 主线程:2021-01-29T11:10:11.090
main 主线程:2021-01-29T11:10:12.091
main 主线程:2021-01-29T11:10:13.092
main 主线程结束

如果注释掉上面的cf.get(),执行结果则是并发打印

applyToEither

这个与acceptEither 只是action操作有返回值与无返回值区别,这里不再演示。

join

如果正常完成则返回结果值,如果异常则返回异常。

public static void     join() throws ExecutionException, InterruptedException {

        Integer result = CompletableFuture.supplyAsync(()->{
            return 1/1;
        }).join();
        System.out.println(result);
    }

handle

当此阶段正常或异常完成时,将使用此阶段的结果和异常作为所提供函数的参数执行。跟上面join的区别在于上面抛出异常,这里进行异常拦截

public static void     handle() throws ExecutionException, InterruptedException {
        CompletableFuture cf = CompletableFuture.supplyAsync(()->{
            return 1/0;
        }).handle((x,y)->{
            System.out.println(y.getMessage());
            return x;
        });
        System.out.println(cf.get());
    }

打印如下:

 

 

该方法可以处理异常与正常结果。并进行再次处理。

 

void obtrudeException(Throwable ex)
强制导致后续调用方法 get()和相关方法抛出给定的异常,无论是否已经完成。
void obtrudeValue(T value)
强制设置或重置随后方法返回的值 get()和相关方法,无论是否已经完成。

这两个方法如描述所示,

runAfterBoth

返回一个新的CompletionStage,当这个和另一个给定的阶段都正常完成时,执行给定的动作。

public static void runAfterBoth() throws ExecutionException, InterruptedException {
        // 1、两个同时执行完成则执行action
        // 2、跟acceptEither是对立的,acceptEither是那个先执行完成再执行action,这个是全部完成。与allOf/anyOf类似
        CompletableFuture cf = CompletableFuture.supplyAsync(()->{
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName()+" "+LocalDateTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return 1;
        }).runAfterBoth(CompletableFuture.supplyAsync(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" "+LocalDateTime.now());
            }
            return 2;
        }),()->{
            System.out.println("执行完成");
        });
        System.out.println(cf.get());
    }

 

 

 方法:thenApply

 类似管道一样的方法值多重处理,代码仅是演示无实际意义

public static void thenApply() throws ExecutionException, InterruptedException {
        // 1、两个同时执行完成则执行action
        // 2、跟acceptEither是对立的,acceptEither是那个先执行完成再执行action,这个是全部完成。与allOf/anyOf类似
        CompletableFuture cf = CompletableFuture.supplyAsync(()->{
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName()+" "+LocalDateTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return 1;
        }).thenApply((x)->{
            return x+1;
        }).thenApply((x)->{
            return x+1;
        });
        System.out.println(cf.get());
    }

方法:thenAcceptBoth

返回一个新的CompletionStage,当这个和另一个给定的阶段都正常完成时,两个结果作为提供的操作的参数被执行。

既:两个结果都完成后一起传给action并执行逻辑。如下代码:

public static void thenAcceptBoth() throws ExecutionException, InterruptedException {
        CompletableFuture cf = CompletableFuture.supplyAsync(()->{
            return 1;
        }).thenAcceptBoth(CompletableFuture.supplyAsync(()->{
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName()+" "+LocalDateTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return 2;
        }),(x,y)->{
            System.out.println("结果:"+x+y);
        });
        System.out.println(cf.get());
    }

 

 打印可以看出,结果的输出为两个任务都执行完毕才打印。

 

 

 

 

 

 

 

 

 

 

 

 

参考文章:https://www.cnblogs.com/cjsblog/p/9267163.html

https://blog.csdn.net/finalheart/article/details/87615546

https://blog.csdn.net/cainiao_user/article/details/76423495

posted @ 2022-09-22 16:42  苦心明  阅读(268)  评论(0)    收藏  举报