Introducción a la programación concurrente en Java (19) Herramienta de programación de tareas asincrónicas CompleteFeature

Java Geek | Autor /   Kang Ran Yi Ye
Este es el 60º artículo original de Java Geek

Lectura relacionada:

Programación concurrente Java (1) Mapa de conocimiento
Programación concurrente Java (2)
Programación concurrente Java atómica (3) Visibilidad
Programación concurrente Java (4)
Programación concurrente Java ordenada (5) Descripción general de los métodos de creación de hilos
Introducción a la programación concurrente Java (6) Uso de sincronización
Introducción a la programación concurrente de Java (7) Escenarios de uso y espera fáciles de entender y notificación
Introducción a la programación concurrente de Java (8) Ciclo de vida del hilo
Introducción a la programación concurrente de Java (9) Punto muerto y punto muerto
Introducción a la programación concurrente de Java (10) Optimización de bloqueo
Java Introducción a la programación concurrente (once) Escenarios de limitación de corriente e implementación del limitador de corriente de resorte de
la programación concurrente de Java (doce) Plantilla de código de modo de productor y consumidor
Programación concurrente de Java (trece) Bloqueo de lectura / escritura y plantilla de caché
Programación concurrente de Java Introducción (catorce)
Escenario de la aplicación CountDownLatch Introducción a la programación concurrente de Java (quince) Escenario de la aplicación CyclicBarrier
Introducción a la programación concurrente de Java (dieciséis) segundos para comprender la diferencia entre el grupo de subprocesos
Introducción a la programación concurrente de Java (diecisiete) un subproceso de imagen maestro clases comunes e interfaces
Concurrencia de Java Introducción a la programación (18) Nueva discusión sobre seguridad de subprocesos


1. Introducción a CompleteFeature

CompleteFeature es una mejora de Feature. Feature solo puede manejar tareas asincrónicas simples, y CompleteFeature puede combinar múltiples tareas asincrónicas en una combinación compleja, admitiendo ejecución en serie, ejecución paralela, y agregación, o agregación, para que pueda manejar tareas relacionadas complejas Realizar la programación.

2. Escenarios comerciales compatibles con CompleteFeature

2.1. Tareas en serie

La tarea en serie se refiere a que la tarea B espera a que la tarea A complete la ejecución. La tarea en serie tiene los siguientes atributos:

Atributos Descripción
Se puede obtener un resultado La tarea B puede obtener el resultado de ejecución de la tarea A como parámetro
B tiene un valor de retorno Si la tarea B tiene un valor de retorno, puede devolver el resultado de la ejecución a través del valor de retorno
Disponible una excepción La tarea B puede obtener la excepción lanzada por la tarea A
Una terminación anormal Cuando la tarea A arroja una excepción, si el programa terminará, si terminará, el programa saldrá, la tarea B no se ejecutará, de lo contrario el programa no saldrá y continuará la ejecución.

Los métodos de tareas en serie compatibles con CompleteFeature son los siguientes:

Método Se puede obtener un resultado B tiene un valor de retorno Disponible una excepción Una terminación anormal
entonces corre No No No Si
entoncesAplicar Si Si No Si
entonces Aceptar Si No No Si
luegoComponer Si Si No Si
whenComplete Si No Si No
excepcionalmente No Si Si No
encargarse de Si Si Si No

Resumen:

  1. La tarea usará los primeros cuatro métodos sin lanzar una excepción; de lo contrario, use los últimos tres métodos.
  2. excepcionalmente equivalente a la parte catch de try {} catch {}, cuando Complete y handle son equivalentes a la parte catch y final de try {} catch {} finalmente {}, la diferencia es que uno tiene un valor de retorno y el otro no tiene ningún valor de retorno.
  3. La diferencia entre thenApply y thenCompose es que thenCompose devuelve CompletableFuture en la tarea B, puede consultar los siguientes ejemplos para comparar las diferencias.

1.2. Y convergencia

Y la relación de agregación significa que la tarea C necesita esperar a que la tarea A o la tarea B se ejecuten antes de ejecutarse. CompleteFeature admite esta relación de la siguiente manera:

Método C recibe el valor de retorno de A o B como parámetro C tiene un valor de retorno
entoncesCombine Si Si
entoncesAceptarBoth Si No
runAfterBoth No No

1.3. O convergencia

O la relación de agregación significa que la tarea C espera a que se ejecute una de las tareas A o B, es decir, C solo necesita esperar a que se ejecute la primera tarea. CompleteFeature admite esta relación de la siguiente manera:

Método C recibe el valor de retorno de A o B como parámetro C tiene un valor de retorno
applyToEither Si Si
aceptar Cualquiera Si No
runAfterEither No No

1.4. Multitarea

CompletableFuture proporciona dos métodos multitarea:

Método Descripción
cualquiera de Después de ejecutar cualquiera de las múltiples tareas, finaliza y se puede obtener el valor de retorno de la primera tarea ejecutada.
todo Se completan varias tareas después de la ejecución y no se puede obtener el valor de retorno de ninguna tarea

Los valores de retorno de todos los métodos anteriores son CompletableFuture, por lo que puede continuar llamando a los métodos descritos anteriormente para realizar una combinación de tareas y combinar flujos de procesamiento de tareas más complejos.

1.5. Método familia

La última tarea en el método anterior se ejecuta en el mismo subproceso que la tarea anterior. También hay un conjunto de métodos en CompletableFuture para que la última tarea se ejecute en un nuevo subproceso, siempre que se agregue el sufijo Async al método original, por ejemplo:

Sincronización Asincrónico
entoncesAplicar thenApplyAsync
entonces Aceptar thenAcceptAsync
entonces corre thenRunAsync
luegoComponer thenComposeAsync

Para más detalles, consulte el código fuente.

2. Ejemplos de código

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompleteFeatureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        simpleTask();
        serialTask();
        andTask();
        orTask();
        complexTask();

        sleep(2000); // 等待子线程结束
        System.out.println("end.");

    }

    private static void simpleTask() throws ExecutionException, InterruptedException {
        // 1. runAsync 执行一个异步任务,没有返回值
        CompletableFuture.runAsync(()-> System.out.println("1. runAsync"));
        sleep(100);

        // 2. supplyAsync 执行一个异步任务,有返回值
        CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
            System.out.println("2.1 supplyAsync task be called");
            sleep(100);
            return "2.2 supplyAsync return value";
        });
        System.out.println("2.3 after supplyAsync");
        System.out.println(future.get());
        sleep(200);
    }

    private static void serialTask() throws ExecutionException, InterruptedException {
        // 3. thenRun
        CompletableFuture.supplyAsync(()->{
            System.out.println("3.1 supplyAsync begin");
            sleep(100);  // 用于证明B等待A结束才会执行
            return "3.2 supplyAsync end";
        }).thenRun(()->{
            System.out.println("3.3 thenRun be called.");
        });
        sleep(200);

        // 4. thenApply
        CompletableFuture<String> future4 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "4.1 apple";
        }).thenApply(returnVal->{
            return "4.2 " + returnVal + "-苹果";
        });
        System.out.println("4.3 get: " + future4.get());
        sleep(100);

        // 5. thenAccept
        CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "5.1 orange";
        }).thenAccept(returnVal->{
            System.out.println("5.2 " + returnVal + "-桔子");
        });
        sleep(100);

        // 6. thenCompose
        CompletableFuture<String> future6 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "6.1 apple";
        }).thenCompose((returnVal)->{
            return CompletableFuture.supplyAsync(()->{
                return "6.2 " + returnVal;
            });
        });
        System.out.println("6.3 get: " + future6.get());
        sleep(100);

        // 7. whenComplete
        CompletableFuture.supplyAsync(()->{
            sleep(100);
            if (true) {  //修改boolean值观察不同结果
                return "7.1 return value for whenComplete";
            } else {
                throw new RuntimeException("7.2 throw exception for whenComplete");
            }
        }).whenComplete((returnVal, throwable)->{
            System.out.println("7.2 returnVal: " + returnVal);  // 可以直接拿到返回值,不需要通过future.get()得到
            System.out.println("7.3 throwable: " + throwable);  // 异步任务抛出异常,并不会因为异常终止,而是会走到这里,后面的代码还会继续执行
        });
        sleep(100);

        // 8. exceptionally
        CompletableFuture<String> future8 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            if (false) {  //修改boolean值观察不同结果
                return "8.1 return value for exceptionally";
            } else {
                throw new RuntimeException("8.2 throw exception for exceptionally");
            }
        }).exceptionally(throwable -> {
            throwable.printStackTrace();
            return "8.3 return value after dealing exception.";
        });
        System.out.println("8.4 get: " + future8.get());
        sleep(100);

        // 9. handle
        CompletableFuture<String> future9 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            if (false) {  //修改boolean值观察不同结果
                return "9.1 return value for handle";
            } else {
                throw new RuntimeException("9.2 throw exception for handle");
            }
        }).handle((retuanVal, throwable)->{
            System.out.println("9.3 retuanVal: " + retuanVal);
            System.out.println("9.4 throwable: " + throwable);
            return "9.5 new return value.";
        });
        System.out.println("9.6 get: " + future9.get());
        sleep(100);
    }

    private static void andTask() throws ExecutionException, InterruptedException {
        // 10. thenCombine 合并结果
        CompletableFuture<String> future10 = CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "10.1 TaskA return value";
        }).thenCombine(CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "10.2 TaskB return value";
        }), (taskAReturnVal, taskBReturnVal) -> taskAReturnVal + taskBReturnVal);
        System.out.println("10.3 get: " + future10.get());
        sleep(200);

        // 11. thenAcceptBoth
        CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "11.1 TaskA return value";
        }).thenAcceptBoth(CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "11.2 TaskB return value";
        }), (taskAReturnVal, taskBReturnVal) -> System.out.println(taskAReturnVal + taskBReturnVal));
        sleep(200);

        // 12. runAfterBoth A,B都执行完后才执行C,C不关心前面任务的返回值
        CompletableFuture.supplyAsync(()->{
            sleep(200);  // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
            System.out.println("12.1 TaskA be called.");
            return "12.2 TaskA return value";
        }).runAfterBoth(CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("12.3 TaskB be called.");
            return "12.4 TaskB return value";
        }), () -> System.out.println("12.5 TaskC be called."));
        sleep(300);
    }

    private static void orTask() throws ExecutionException, InterruptedException {
        // 13. applyToEither 使用A,B两个异步任务优先返回的结果
        CompletableFuture<String> future13 = CompletableFuture.supplyAsync(()->{
            sleep(200);  // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
            System.out.println("13.1 TaskA be called"); // 用于证明拿到B的结果后,A还会继续执行,并不会终止
            return "13.2 TaskA return value";
        }).applyToEither(CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "13.3 TaskB return value";
        }), (returnVal) -> returnVal);
        System.out.println("13.4 get: " + future13.get());
        sleep(300);

        // 14. acceptEither 使用A,B两个异步任务优先返回的结果
        CompletableFuture.supplyAsync(()->{
            sleep(200);  // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
            return "14.1 TaskA return value";
        }).acceptEither(CompletableFuture.supplyAsync(()->{
            sleep(100);
            return "14.2 TaskB return value";
        }), (returnVal) -> System.out.println(returnVal));
        sleep(300);

        // 15. runAfterEither A,B任意一个执行完后就执行C,C不关心前面任务的返回值
        CompletableFuture.supplyAsync(()->{
            sleep(200);  // 虽然这个任务先执行,但是执行时间比下面的任务长,所以最后会使用下面的返回结果
            System.out.println("15.1 TaskA be called.");
            return "15.2 TaskA return value";
        }).runAfterEither(CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("15.3 TaskB be called.");
            return "15.4 TaskB return value";
        }), () -> System.out.println("15.5 TaskC be called."));
        sleep(300);
    }

    private static void complexTask() throws ExecutionException, InterruptedException {
        // 16. anyOf
        CompletableFuture future16 = CompletableFuture.anyOf(CompletableFuture.supplyAsync(()->
        {
            sleep(300);
            System.out.println("16.1 TaskA be called.");
            return "16.2 TaskA return value.";
        }), CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("16.3 TaskB be called.");
            return "16.4 TaskB return value.";
        }));
        System.out.println("16.5 get: " + future16.get());
        sleep(400);

        // 17. allOf
        CompletableFuture<Void> future17 = CompletableFuture.allOf(CompletableFuture.supplyAsync(()->
        {
            sleep(300);
            System.out.println("17.1 TaskA be called.");
            return "17.2 TaskA return value.";
        }), CompletableFuture.supplyAsync(()->{
            sleep(100);
            System.out.println("17.3 TaskB be called.");
            return "17.4 TaskB return value.";
        }));
        System.out.println("17.5 get: " + future17.get()); // allOf没有返回值
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }
}
复制代码

3. Resumen

CompleteFeature admite la programación compleja de tareas asincrónicas, admite múltiples tareas en serie, paralelas y de agregación.Cuando varias tareas asincrónicas tienen dependencias, la programación de tareas a través de CompleteFeature puede simplificar enormemente el código y mejorar el rendimiento de la ejecución.

fin.


<-Lee la marca, como a la izquierda !


Supongo que te gusta

Origin juejin.im/post/5e99c652518825737f1a7936
Recomendado
Clasificación