代码改变世界

玩转CompletableFuture线程异步编排,看这一篇就够了

2024-02-18 20:03  l_v_y_forever  阅读(438)  评论(1编辑  收藏  举报

转载自:https://blog.csdn.net/w306026355/article/details/109707269

1、CompletableFuture介绍

CompletableFuture可用于线程异步编排,使原本串行执行的代码,变为并行执行,提高代码执行速度。

学习异步编排先需要学习线程池和lambda表达式相关知识,学习线程池可以移步我的另一篇博客

ThreadPoolExecutor线程池理解

2、CompletableFuture使用

说明:使用CompletableFuture异步编排大多方法都会有一个重载方法,会多出一个executor参数,用来传来自定义的线程池,如果不传就会使用默认的线程池。

下文举例都是使用自定义线程池,不再做特殊说明。

贴出自定义线程池的代码

ThreadPoolExecutor executor = new ThreadPoolExecutor(
        10,
        50,
        60,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(100000),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.1、创建异步编排对象

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
 
  • 1
  • 2
  • 3
  • 4
  • 5

有两种格式,一种是supply开头的方法,一种是run开头的方法

  • supply开头:这种方法,可以返回异步线程执行之后的结果

  • run开头:这种不会返回结果,就只是执行线程任务

举例:

// 异步起线程执行业务 无返回值
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getId());
    int i = 10 / 2;
    System.out.println("运行结果:" + i);
}, executor);


//异步起线程执行业务 有返回值
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getId());
    int i = 10 / 0;
    System.out.println("运行结果:" + i);
    return i;
}, executor).whenComplete((res,exc)->{
    // 可以接收到返回值和异常类型,但是无法处理异常
    System.out.println("异步任务成功完成了...结果是:" + res + ";异常是:" + exc);
}).exceptionally(throwable -> {
    // 处理异常,返回一个自定义的值,和上边返回值无关。
    return 10;
});

//方法执行完成后的处理
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getId());
    int i = 10 / 0;
    System.out.println("运行结果:" + i);
    return i;
}, executor).handle((res,thr)->{
    // 无论线程是否正确执行,都会执行这里,可以对返回值进行操作。
    if(res != null){
        return res * 2;
    }
    if(thr != null){
        return 0;
    }
    return 0;
});
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

这里出现了三个新方法

CompletableFuture.whenComplete():用于接收带有返回值的CompletableFuture对象,无法修改返回值。

CompletableFuture.exceptionally():用于处理异常,只要异步线程中有抛出异常,则进入该方法,修改返回值。

CompletableFuture.handle():用于处理返回结果,可以接收返回值和异常,可以对返回值进行修改。

2.2、线程串行方法

// 使线程串行执行,无入参,无返回值
public CompletableFuture<Void> thenRun(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor);

// 使线程串行执行,有入参,无返回值
public CompletableFuture<Void> thenAccept(Consumer<? super T> action);
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);

// 使线程串行执行,有入参,有返回值
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

解释:

  • 不以Async结尾的方法,都是在执行串行的时候,使用执行上一个方法的线程,也就是说从头串行到最后一个任务,使用的是同一个线程。
  • 而以Async结尾的方法,每串行一个方法,都会使用一个新线程。下文不再解释。

例子:

// 两个任务串行执行,任务2不用任务1的返回值,并且任务2无返回值。
CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1:当前线程:" + Thread.currentThread().getId());
    int i = 10 / 4;
    System.out.println("任务1:运行结果:" + i);
    return i;
}, executor).thenRunAsync(() -> {
    System.out.println("任务2启动了");
}, executor);

// 两个任务串行执行,任务2要使用任务1的返回值,并且任务2无返回值。
CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1:当前线程:" + Thread.currentThread().getId());
    int i = 10 / 4;
    System.out.println("任务1:运行结果:" + i);
    return i;
}, executor).thenAcceptAsync(res -> {
    System.out.println("任务2启动了" + res);
}, executor);

// 两个任务串行执行,任务2要使用任务1的返回值,并且返回任务2的返回值。
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1:当前线程:" + Thread.currentThread().getId());
    int i = 10 / 4;
    System.out.println("任务1:运行结果:" + i);
    return i;
}, executor).thenApplyAsync(res -> {
    System.out.println("任务2启动了" + res);
    return "Hello" + res;
}, executor);
// 有返回值时,需要使用CompletableFuture.get()方法,等待异步线程执行结束,从而获取到异步线程的返回值。
String s = future3.get();
System.out.println(s);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2.3、两任务并行执行完成,再执行新任务

解释:两任务并行执行,并且都执行完成之后,串行另一个任务,也就是说在两个任务执行并行执行完成之后,需要再执行另一个任务。

// 线程并行执行完成,并且执行新任务action,新任务无入参,无返回值
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor);

// 线程并行执行完成,并且执行新任务action,新任务有入参,无返回值
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor);

// 线程并行执行完成,并且执行新任务action,新任务有入参,有返回值
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

例子:

// 任务1
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1线程:" + Thread.currentThread().getId());
    int i = 10 / 4;
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("任务1结束:");

    return i;
}, executor);

// 任务2
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务2线程:" + Thread.currentThread().getId());
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("任务2结束");

    return "Hello";
}, executor);

// 任务1和任务2都完成,在不使用任务1和任务2的返回值情况下执行任务3,并且任务3没有返回值
CompletableFuture<Void> future1 = future01.runAfterBothAsync(future02,
                                                             () -> System.out.println("任务3开始"), executor);

// 任务1和任务2都完成,使用任务1和任务2的返回值情况下执行任务3,并且任务3没有返回值
CompletableFuture<Void> future2 = future01.thenAcceptBothAsync(future02,
                                                               (f1, f2) -> System.out.println("任务3开始,之前的结果" + f1 + "-->" + f2),
                                                               executor);

// 任务1和任务2都完成,使用任务1和任务2的返回值情况下执行任务3,并且任务3有返回值
CompletableFuture<String> future3 = future01.thenCombineAsync(future02,
                                                              (f1, f2) -> f1 + ":" + f2 + "->haha", 
                                                              executor);
String str = future3.get();
System.out.println(str);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

2.4 、两任务并行执行,其中一个执行完,就执行新任务。

解释:两任务并行执行,只要其中有一个执行完,就开始执行新任务。

// 任务并行执行,只要其中有一个执行完,就开始执行新任务action,新任务无入参,无返回值
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action);
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor);

// 任务并行执行,只要其中有一个执行完,就开始执行新任务action,新任务有入参(入参类型为Object,因为不确定是哪个任务先执行完成),无返回值
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action);
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action);
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor);

// 任务并行执行,只要其中有一个执行完,就开始执行新任务action,新任务有入参(入参类型为Object,因为不确定是哪个任务先执行完成),有返回值
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn);
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn);
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

例子:

// 任务1
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1线程:" + Thread.currentThread().getId());
    int i = 10 / 4;
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("任务1结束:");

    return i;
}, executor);

// 任务2
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务2线程:" + Thread.currentThread().getId());
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("任务2结束");

    return "Hello";
}, executor);

// 任务1和任务2并行执行,只要有一个执行完成,就执行任务3,不使用任务1 或 任务2线程的结果,并且任务3没有返回值
future01.runAfterEitherAsync(future02,()-> System.out.println("任务3开始,之前的结果"), executor);

// 任务1和任务2并行执行,只要有一个执行完成,就执行任务3,使用任务1 或 任务2线程的结果,并且任务3没有返回值
future01.acceptEitherAsync(future02, (res)-> System.out.println("任务3开始,之前的结果" + res), executor);

// 任务1和任务2并行执行,只要有一个执行完成,就执行任务3,使用任务1 或 任务2线程的结果,并且任务3有返回值
CompletableFuture<Object> future = future01.applyToEitherAsync(future02, (res) -> {
    System.out.println("任务3开始,之前的结果" + res);
    return res.toString() + "->哈哈";
}, executor);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

2.5、多任务组合(只要有一个执行完就返回)

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
 
  • 1

例子:

CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> "任务1", executorService);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> "任务2", executorService);
CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "任务3";
}, executorService);

// 只要异步线程队列有一个任务率先完成就返回,这个特性可以用来获取最快的那个线程结果。
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future01, future02, future03);

// 获取若干个任务中最快完成的任务结果
// .join()和.get()都会阻塞并获取线程的执行情况
// .join()会抛出未经检查的异常,不会强制开发者处理异常 .get()会抛出检查异常,需要开发者处理
Object o1 = anyOf.get();
Object o2 = anyOf.join();
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2.6、多任务组合(全部执行完才返回)

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
 
  • 1

例子:

CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> "任务1", executorService);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> "任务2", executorService);
CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "任务3";
}, executorService);

// 串联起若干个线程任务, 没有返回值
CompletableFuture<Void> all = CompletableFuture.allOf(future01, future02, future03);
// 等待所有线程执行完成
// .join()和.get()都会阻塞并获取线程的执行情况
// .join()会抛出未经检查的异常,不会强制开发者处理异常 .get()会抛出检查异常,需要开发者处理
all.join();
all.get();
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18