¿Conoces la nueva característica de concurrencia de Java8 CompletableFuture?

¡Continúe creando, acelere el crecimiento! Este es el primer día de mi participación en el "Nuggets Daily New Plan · June Update Challenge", haz clic para ver los detalles del evento

1. ¿Qué es CompletableFuture?

¿Tienen la sensación de que mientras dedicamos mucho tiempo al desarrollo empresarial durante todo el día, no prestamos atención a algunas actualizaciones técnicas e ignoramos muchos métodos prácticos y sencillos? En el pasado, cuando realizábamos tareas asincrónicas, estábamos acostumbrados a usar Callable o Para implementar la interfaz Runnable, hoy hablaremos sobre la clase CompletableFuture que es diferente a ella.

CompletableFuture ha mejorado la interfaz Future. En comparación con la interfaz Callable/Runnable, admite llamadas en cadena multitarea, combinación y procesamiento simultáneo multitarea. Muchas veces en el proceso de diseño queremos obtener directamente el resultado de una tarea asíncrona y pasarlo a la siguiente tarea para continuar con el proceso posterior, en este momento entra el rol de CompletableFuture.

  • Diagrama de clases CompletableFuture

Como puede verse en el siguiente diagrama de clases, CompletableFuture implementa las interfaces Future y CompletionStage, y Future proporciona la función de obtener el resultado de la ejecución de la tarea y el estado de ejecución de la tarea. CompletionStage representa la etapa de ejecución de una tarea y proporciona muchos métodos para admitir la función de agregación de varias tareas.

CompletableFuturo.jpg

2. Instrucciones para usar los métodos de CompletableFuture

2.1 La clase CompletableFuture proporciona varios métodos estáticos para operaciones asincrónicas:

supplyAsync y runAsync se utilizan principalmente para crear eventos asincrónicos.

  • supplyAsync带有返回值的异步任务,支持在默认线程池ForkJoinPool.commonPool()中完成异步任务,也可以使用自定义线程池执行异步任务,结果返回一个新的CompletableFuture,返回结果类型U。最终的任务执行结果可通过返回CompletableFuture对象的 get()/join() 方法获取返回值。
// 使用默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {...}
// 使用自定义线程池Executor
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) {...}

// ====================================demo华丽分割线============================================
CompletableFuture<String> supplyAsyncFuture = CompletableFuture.supplyAsync(() -> {
    log.info("executing supplyAsync task ...");
    return "this is supplyAsync";
});
// 进入阻塞获取异步任务结果
log.info(supplyAsyncFuture.get());  // 输出结果:this is supplyAsync
复制代码
  • runAsync不带返回值的异步任务,支持在默认线程池ForkJoinPool.commonPool()中完成异步任务,也可以使用自定义线程池执行异步任务,结果返回一个新的CompletableFuture,返回结果类型为Void,也就是无返回值。
public static CompletableFuture<Void> runAsync(Runnable runnable) {...}
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {...}

// ====================================demo华丽分割线============================================
CompletableFuture<Void> runAsyncFuture = CompletableFuture.runAsync(() -> {
    log.info("executing runAsync task ...");
});
runAsyncFuture.get();
复制代码
  • allOf:多个CompletableFuture任务并发执行,所有CompletableFuture任务完成时,返回一个新的CompletableFuture对象,其返回值为Void,也就是无返回值。
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {...}

// ====================================demo华丽分割线============================================
// allOf,可传递返回值不同类型的future,最终结果按自己设计预期处理即可
CompletableFuture<String> cf11 = CompletableFuture.supplyAsync(() -> {
    log.info("executing supplyAsync task cf11 ...");
    return "this is supplyAsync";
});
CompletableFuture<String> cf12 = CompletableFuture.supplyAsync(() -> {
    log.info("executing supplyAsync task cf12 ...");
    return "this is supplyAsync";
});
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(cf11, cf12);
allOfFuture.get();
复制代码
  • anyOf:多个CompletableFuture任务并发执行,只要有一个CompletableFuture任务完成时,就会返回一个新的CompletableFuture对象,并返回该CompletableFuture执行完成任务的返回值。
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {...}

// ====================================demo华丽分割线============================================
CompletableFuture<String> cf21 = CompletableFuture.supplyAsync(() -> {
    log.info("executing supplyAsync task cf21 ...");
    return "this is supplyAsync cf21";
});
CompletableFuture<String> cf22 = CompletableFuture.supplyAsync(() -> {
    log.info("executing supplyAsync task cf22 ...");
    return "this is supplyAsync cf22";
});
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(cf21, cf22);
log.info("{}", anyOfFuture.get());  // 输出结果:this is supplyAsync cf21或cf22
复制代码

2.2 获取异步任务执行结果的方法 get()/join()

join()和get()方法都是CompletableFuture对象基于阻塞的方式来获取异步任务执行结果。

  • get方法会抛出显示异常必须捕获处理,任务允许被中断抛出InterruptedException异常,通过带有超时时间的阻塞方式获取异步任务执行结果,超时等待无结果则中断任务抛出TimeoutException异常。
  • join方法会抛出未检查异常,与get()方法不同的是join()方法不允许被中断。
// 可中断,可设置超时时间
public T get() throws InterruptedException, ExecutionException {...}
public T get(long timeout, TimeUnit unit) throws InterruptedException, 
                ExecutionException, TimeoutException {...}
/**
* 不可中断
*/
public T join() {...}
复制代码

3.CompletionStage的方法使用说明

CompletionStage表示一个任务的执行阶段,每个任务都会返回一个CompletionStage对象,可以对多个CompletionStage对象进行串行、并行或者聚合的方式来进行下阶段的操作,也就是说实现异步任务的回调功能。CompletionStage总共提供了38个方法来实现多个CompletionStage任务的各种操作, 接下来我们就针对这些方法分类来了解一下。

以下类型均有三种使用方式:

  • thenAccept:方法名不带Async的使用主线程同步执行回调函数,不做异步处理
  • thenAcceptAsync:方法名带Async,但是无executor参数的,使用默认线程池ForkJoinPool.commonPool异步执行任务
  • thenAcceptAsync:方法名带Async,有executor参数的,使用自定义线程池异步执行任务

3.1 纯消费类型

  • 依赖单个任务完成(thenAccept):由上一个CompletionStage任务执行完成的结果传递到action进行回调处理,即仅仅消费了上一个CompletionStage任务的返回值,回调处理结果无返回值。
// 不使用线程池,仅依赖当前线程执行,不做异步
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
// 使用默认线程池ForkJoinPool.commonPool执行任务
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
// 使用自定义线程池执行任务
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture.supplyAsync(() -> "this is supplyAsync")
        .thenAcceptAsync((result) -> {
            log.info("{} thenAcceptAsync", result);
        }).join();
        
// 输出结果:this is supplyAsync thenAcceptAsync
复制代码
  • 依赖两个任务都完成(thenAcceptBoth):两个CompletionStage任务并发执行,必须都完成了才执行action回调处理,即仅仅消费了两个CompletionStage任务的返回值,回调处理结果无返回值。
/**
* 额外多了CompletionStage参数表示CompletionStage任务依赖的另一个CompletionStage任务
* action接收两个参数,分别表示两个CompletionStage任务的返回值
*/
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other, 
                            BiConsumer<? super T, ? super U> action);
// 原理同上,使用默认线程池执行异步任务
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, 
                            BiConsumer<? super T, ? super U> action);
// 原理同上,使用自定义线程池执行异步任务
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, 
                            BiConsumer<? super T, ? super U> action, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture<String> cf311 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf311");
CompletableFuture<String> cf312 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf312");
cf311.thenAcceptBothAsync(cf312, (r1, r2) -> {
   log.info("{} and {}", r1, r2);
}).join();
// 输出结果:this is supplyAsync cf311 and this is supplyAsync cf312
复制代码
  • 依赖两个任务中的任何一个完成(acceptEither):两个CompletionStage任务并发执行,只要其中一个先完成了就携带返回值执行action回调处理,即仅仅消费了优先完成的CompletionStage任务的返回值,回调处理结果无返回值。
/**
* 类似thenAcceptBothAsync,只不过acceptEither只需两个任务中的其中一个完成即可回调action
* action中的值为两个任务中先执行完任务的返回值
*/
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,
                             Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,
                             Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,
                             Consumer<? super T> action, Executor executor);
                             
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf311 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf311");
CompletableFuture<String> cf312 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf312");
cf311.acceptEitherAsync(cf312, (r) -> {
    log.info(r); // 输出结果:this is supplyAsync cf311或cf312
}).join();
复制代码

3.2 有返回值类型

  • 依赖单个任务完成(thenApply):由上一个CompletionStage任务执行完成的结果传递到action进行回调处理,即不止消费了上一个CompletaionStage任务的返回值,同时回调处理结果也有返回值
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture<String> cf32 = CompletableFuture.supplyAsync(() -> "this is supplyAsync")
        .thenApplyAsync(result -> result + " and thenApplyAsync");
log.info(cf32.join());  // 输出结果:this is supplyAsync and thenApplyAsync
复制代码
  • 依赖两个任务都完成(thenCombine):两个CompletionStage任务并发执行,必须都完成了才执行action回调处理,即不止消费了两个CompletaionStage任务的返回值,同时回调处理结果也有返回值。
public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,
                             BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
                             BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,
                             BiFunction<? super T,? super U,? extends V> fn, Executor executor);
                             
// ====================================demo华丽分割线============================================
CompletableFuture<String> cf321 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf321");
CompletableFuture<String> cf322 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf322");
CompletableFuture<String> thenCombineFuture = cf321.thenCombineAsync(cf322, (r1, r2) -> {
    return r1 + " and " + r2;
});
log.info(thenCombineFuture.join());
// 输出结果:this is supplyAsync cf321 and this is supplyAsync cf322
复制代码
  • 依赖两个任务中的任何一个完成(applyToEither):两个CompletionStage任务并发执行,只要其中一个任务执行完成就会action回调处理,即不止消费了优先完成的CompletionStage的返回值,同时回调处理结果也有返回值。
// 原理同3.1的acceptEither,只不过applyToEither任务执行完成会返回一个带有返回值的CompletionStage
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,
                             Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,
                             Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,
                             Function<? super T, U> fn, Executor executor);
                             
// ====================================demo华丽分割线============================================

CompletableFuture<String> cf321 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf321");
CompletableFuture<String> cf322 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf322");
CompletableFuture<String> thenCombineFuture = cf321.applyToEitherAsync(cf322, (r) -> {
    return r;
});
log.info(thenCombineFuture.join());
// 输出结果:this is supplyAsync cf321或cf322
复制代码

3.3 不消费也不返回类型

  • 依赖单个任务完成(thenRun):单个CompletionStage任务执行完成回调action处理,即执行action回调方法无参数,回调处理结果也无返回值。
// 上一个CompletionStage任务执行完成后直接回调action处理,无返回值
public CompletionStage<Void> thenRun(Runnable action);
// 同上,使用默认线程池执行action处理
public CompletionStage<Void> thenRunAsync(Runnable action);
// 同上,使用自定义线程池执行action处理
public CompletionStage<Void> thenRunAsync(Runnable action, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture.runAsync(() -> {
    // TODO
}).thenRunAsync(() -> {
    log.info("this is thenRunAsync");  // 输出结果:this is thenRunAsync
}).join();
复制代码
  • 依赖两个任务都完成(runAfterBoth):两个CompletionStage任务并发执行,必须两个任务都完成才执行action回调处理,即执行action回调方法无参数,回调处理结果也无返回值。
// 原理同3.1的thenAcceptBoth,只不过runAfterBoth的action回调处理不接收参数且任务执行完成无返回值
public CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action);
// 同上,使用默认线程池执行action处理
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action);
// 同上,使用自定义线程池执行action处理
public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture<String> cf331 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf331");
CompletableFuture<String> cf332 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf332");
cf331.runAfterBoth(cf332, () -> {
    log.info("this is runAfterBoth");
}).join();
// 输出结果:this is runAfterBoth
复制代码
  • 依赖两个任务中的任何一个完成(runAfterEither):两个CompletionStage任务并发执行,只需其中任何一个任务完成即可回调action处理,即执行action回调方法无参数,回调处理结果也无返回值。
public CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture<String> cf331 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf331");
CompletableFuture<String> cf332 = CompletableFuture.supplyAsync(() -> "this is supplyAsync cf332");
cf331.runAfterEitherAsync(cf332, () -> {
    log.info("this is runAfterEitherAsync");
}).join();
// 输出结果:this is runAfterEitherAsync
复制代码

3.4 组合类型

  • thenCompose:存在先后关系的两个任务进行串行组合,由第一个CompletionStage任务执行结果作为参数传递给第二个CompletionStage任务,最终返回第二个CompletionStage。
public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture<String> supplyFuture = CompletableFuture.supplyAsync(() -> {
    return "this is supplyAsync";
});
CompletableFuture<String> thenComposeFuture = supplyFuture.thenComposeAsync((r) -> {
    return CompletableFuture.supplyAsync(() -> {
        return r + " and this is thenComposeAsync";
    });
});
log.info(thenComposeFuture.join());
// 输出结果:this is supplyAsync and this is thenComposeAsync
复制代码

3.5 任务事件类型

CompletionStage接口也支持类似我们常用的try-catch-finally中的finally的作用,无论这个任务的执行结果是正常还是出现异常的情况,都必须要去执行的一个代码块。在CompletionStage接口提供了以下两种接口回调的形式(whenComplete、handle),并支持主线程同步执行同时也支持使用默认线程池,或者使用自定义线程池去异步执行最终的回调处理。例如我们一个事务操作,无论这段代码执行是否成功,我们都必须要去关闭事务。

  • 任务完成事件(whenComplete):结果无返回值,若出现异常执行完whenComplete回调处理完成后将中断主线程的运行
// 1.whenComplete回调函数中Throwable对象不对空代表出现异常,为空则表示无异常
public CompletionStage<T> whenComplete(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action);
public CompletionStage<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture<String> whenCompleteFufute = CompletableFuture.supplyAsync(() -> {
    int a = 0;
    int b = 100 / a;
    return "this is supplyAsync normal";
}).whenCompleteAsync((r, th) -> {
    if (th != null) {
        log.error("this is whenCompleteAsync error");
    }
    else {
        log.info("this is whenCompleteAsync success");
    }
});
log.info(whenCompleteFufute.join());  // 输出结果:this is whenCompleteAsync error
复制代码
  • 任务完成回调事件(handle):结果有返回值,若出现异常执行完handle回调处理完成后将继续执行主线程的后续操作,不中断主线程运行
// 2.handle回调函数中Throwable对象不对空代表出现异常,为空则表示无异常
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor);

// ====================================demo华丽分割线============================================
CompletableFuture<String> whenCompleteFufute = CompletableFuture.supplyAsync(() -> {
    int a = 0;
    int b = 100 / a;
    return "this is supplyAsync normal";
}).handleAsync((r, th) -> {
    if (th != null) {
        return "this is handleAsync error";
    }
    else {
        return "this is handleAsync success";
    }
});
log.info(whenCompleteFufute.join());
// 输出结果:this is handleAsync error
log.info("main thread is running");
// 输出结果:main thread is running
复制代码

4.CompletionStage异常处理方法

  • excepcionalmente : siempre que sea un programa, habrá excepciones, como una tarea de CompletionStage. Si hay una excepción durante la ejecución, para garantizar que el programa pueda procesar la lógica comercial normalmente en condiciones anormales, podemos usar excepcionalmente aquí para hacer frente a las devoluciones de llamada de excepción. Cuando ocurre una excepción en la tarea CompletionStage, la devolución de llamada se activará excepcionalmente; de ​​lo contrario, la ejecución normal de la tarea CompletionStage no realizará el procesamiento de devolución de llamada de excepción.
public CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);

// ====================================demo华丽分割线============================================
CompletableFuture<String> exceptionallyFuture = CompletableFuture.supplyAsync(() -> {
    int a = 0;
    int b = 10 / a;  // 除数为0将抛异常
    return "this is supplyAsync normal";
}).exceptionally(th -> {
    log.error("exception:{}", th.getMessage());
    return "this is exceptionally";
});
log.info(exceptionallyFuture.join());  // 输出结果:this is exceptionally
复制代码

Nota : Las siguientes dos situaciones pueden ser raras en el proceso de desarrollo real, pero aun así deben recordarse aquí para evitar defectos de diseño debido a una preparación insuficiente al final.

  • Cuando whenCompleteAsync y excepcionalmente se usan al mismo tiempo, si ocurre una excepción, porque excepcionalmente tiene un valor de retorno, whenCompleteAsync se ejecuta primero y luego excepcionalmente.
  • Cuando handleAsync y excepcionalmente aparecen al mismo tiempo, porque handleAsync ya contiene todas las operaciones de excepcionalmente, es decir, la devolución de llamada handleAsync tiene un valor de retorno, y hay un objeto de excepción Throwable que puede manejar excepciones, por lo que cuando los dos aparecen en el mismo tiempo, excepcionalmente fallará.

5. Resumen de tipos de métodos

De acuerdo con los métodos anteriores, podemos concluir que estas tareas en realidad se dividen en tres categorías, que son equivalentes a realizar las operaciones de cadenas de tareas múltiples, tareas múltiples en paralelo y agregación de tareas múltiples a través del mecanismo de devolución de llamada de CompletionStage. se utiliza para tareas asincrónicas El procesamiento proporciona un modelo de programación más potente. Por lo tanto, la clase CompletableFuture proporcionada por java8 proporciona algo de programación en cadena en comparación con la interfaz Future original, lo que nos ahorra muchos pasos complicados de operaciones de devolución de llamadas de tareas asincrónicas, lo que nos permite codificar a los agricultores para generar productos con mayor eficiencia.

Supongo que te gusta

Origin juejin.im/post/7102277353614606344
Recomendado
Clasificación