CompletableFuture manejo de excepciones y runAsync thenRun

Miguel:

Digamos que tengo este código de ejemplo y una excepción se encuentra en el interior runAsync. Mi pregunta es esta excepción sería evitar que el thenRunde la silla eléctrica como thenRuncarreras en la misma rosca que el método llamador de este código.

private void caller() {
    CompletableFuture.runAsync(() -> {
          try {
              // some code
          } catch (Exception e) {
              throw new CustomException(errorMessage, e);
          }
         }, anInstanceOfTaskExecutor).thenRun(
         // thenRun code
     ));
}

Ya pasé por este hilo y se explica cómo se puede manejar excepciones producidas a partir de bloques asíncronos (es decir, mediante el bloqueo y el uso join). Quiero saber si el código dentro thenRundel bloque se ejecuta o no si CompletableFuture completesExceptionally.

actualización :

Me encontré con algo de código para probar esto:

CompletableFuture.runAsync(() -> {
      List<Integer> integerList = new ArrayList<>();
      integerList.get(1);    // throws exception
    }).thenRun(() -> {
      System.out.println("No exception occurred");
    });

No se imprime nada y que significa una excepción no 'se propagan hasta / alcance' hilo del método de la persona que llama desde el bloque asíncrono. Me entender el comportamiento esperado aquí ahora, pero tengo las siguientes preguntas:

  1. ¿Por qué es silencio fallando a pesar de que la CompletableFuture completesExceptionally?
  2. ¿Cómo funciona en segundo plano?
  3. Es porque estos dos hilos (hilo de rosca del interlocutor e asíncrono) tienen su propio espacio de pila?
repollo:

Información general

La documentación de los CompletionStageexplica las reglas generales de la interfaz:

La fase de un cálculo posiblemente asíncrono, que realiza una acción o calcula un valor cuando otro CompletionStagese complete. Una etapa ultima al término de su cálculo, pero esto puede en otras etapas que dependen a su vez del gatillo. La funcionalidad se define en esta interfaz tarda sólo unos pocos formas básicas, que se expanden hacia fuera a un conjunto más amplio de métodos para capturar una gama de estilos de uso:

  • El cálculo realizado por una etapa se puede expresar como una Function, Consumero Runnable(usando métodos con nombres incluyendo aplica , aceptar , o carrera , respectivamente) dependiendo de si se requiere argumentos y / o produce resultados. Por ejemplo:

    stage.thenApply(x -> square(x))
         .thenAccept(x -> System.out.print(x))
         .thenRun(() -> System.out.println());
    

    Una forma adicional ( de composición ) permite la construcción de tuberías de cálculo de funciones que devuelven etapas de terminación.

    Cualquier argumento para el cálculo de una etapa es el resultado del cálculo de una etapa de activación.

  • Uno de ejecución etapa puede ser provocado por la finalización de una sola etapa, o ambos de dos etapas, o cualquiera de las dos etapas. Dependencias en una sola etapa están dispuestos usando métodos con prefijo a continuación . Los provocados por la finalización de los dos dos etapas pueden combinar sus resultados o efectos, utilizando métodos correspondientemente con nombre. Los provocados por cualquiera de las dos etapas no ofrecen ninguna garantía sobre cuál de los resultados o efectos se utilizan para el cálculo de la etapa dependiente.

  • Dependencias entre las etapas de control de la activación de los cálculos, pero no garantizan de otra forma cualquier orden particular. Además, la ejecución de los cálculos de una nueva etapa puede estar dispuesto en cualquiera de tres maneras: de ejecución por defecto, defecto de ejecución asíncrono (usando métodos con sufijo asíncrono que emplean instalaciones de ejecución asíncrono predeterminado de la etapa), o personalizado (a través de un suministra Executor). Las propiedades de ejecución de los modos predeterminados y asincrónicos se especifican mediante CompletionStageimplementaciones, no esta interfaz. Los métodos con explícitos Executorargumentos pueden tener propiedades ejecución arbitraria, y ni siquiera podrían apoyar la ejecución concurrente, pero están dispuestos para procesar de una manera que se adapte a la asincronía.

  • Dos formas de método ( handley whenComplete) apoyan cálculo incondicional si la etapa de activación completada normalmente o excepcionalmente. Método exceptionallysoportes computación solamente cuando la etapa de activación se completa con carácter excepcional, el cálculo de un resultado de reemplazo, de manera similar a la de Java [sic] catch de palabras clave. En todos los demás casos, si el cálculo de una etapa termina abruptamente con una excepción (sin marcar) o error, a continuación, todas las etapas dependientes que requiere su finalización completa excepcionalmente, así, con una CompletionExceptionque sostiene la excepción como su causa. Si una etapa depende tanto de dos etapas, y ambos completa excepcionalmente, a continuación, la CompletionExceptionpuede corresponder a cualquiera de estas excepciones. Si una etapa depende de cualquierade los otros dos, y sólo uno de ellos se completa con carácter excepcional, se hacen sin garantías acerca de si la etapa dependientes terminen normalmente o excepcional. En el caso del método whenComplete, cuando la acción suministrado en sí encuentra una excepción, entonces la etapa completa excepcionalmente con esta excepción a menos que la etapa fuente también completó excepcionalmente, en cuyo caso se le da la finalización excepcional de la etapa de fuente de preferencia y se propaga a la etapa dependiente .

Todos los métodos se adhieren a la anterior activación, ejecución, y las especificaciones de terminación excepcionales (que no se repiten en las especificaciones individuales del procedimiento). [...]

[...]

Y documentación de los CompletableFutureexplica las reglas de roscado (y otras políticas) donde, como se documentó anteriormente, algunos de los cuales se dejan a las implementaciones de CompletionStage:

Una Futureque puede ser completado de forma explícita (fijando su valor y el estado), y puede ser utilizado como CompletionStage, apoyando las funciones dependientes y las acciones que se disparan tras su finalización.

Cuando dos o más hilos intentan complete, completeExceptionallyo canceluna CompletableFuture, sólo uno de ellos tiene éxito.

Además de estos y otros métodos para la manipulación directa de estado y los resultados, CompletableFutureimplementa la interfaz CompletionStagecon las siguientes políticas:

  • Acciones suministrados para las terminaciones dependientes de los métodos no asincrónicos pueden ser realizadas por el hilo que completa la corriente CompletableFuture, o por cualquier otra persona que llama de un método de terminación.

  • Todos los métodos asincrónicos sin una explícita Executorargumento se realizan usando la ForkJoinPool.commonPool()(a menos que no soporta un nivel de paralelismo de al menos dos, en cuyo caso, un nuevo Threadse crea para ejecutar cada tarea). Esto puede ser anulado para los métodos no estáticos en subclases mediante la definición de método defaultExecutor(). Para simplificar la supervisión, depuración y seguimiento, todas las tareas asíncronas generados son instancias de la interfaz de marcador CompletableFuture.AsynchronousCompletionTask. Operaciones con retardos pueden utilizar métodos de adaptadores definidos en esta clase, por ejemplo: supplyAsync(supplier, delayedExecutor(timeout, timeUnit)). Para apoyar a los métodos con los retrasos y tiempos de espera, esta clase mantiene a lo sumo un hilo de utilidad para la activación y cancelación de acciones, no para ejecutarlos.

  • Todos los CompletionStagemétodos se aplican con independencia de otros métodos públicos, por lo que el comportamiento de un método no se ve afectada por las anulaciones de otros en subclases.

  • Todos los CompletionStagemétodos devuelven CompletableFutures. Para restringir usos a sólo aquellos métodos definidos en la interfaz CompletionStage, el método de uso minimalCompletionStage(). O para asegurar que sólo los clientes que no se modifican a sí mismos un futuro, utilice el método copy().

CompletableFuturetambién lleva a cabo Futurecon las siguientes políticas:

  • Puesto que (a diferencia de FutureTask) esta clase no tiene control directo sobre el cálculo que hace que se completó, la cancelación es tratada como otra forma de realización excepcional. Método canceltiene el mismo efecto que completeExceptionally(new CancellationException()). Método isCompletedExceptionally()se puede utilizar para determinar si un CompletableFutureterminado en cualquier forma excepcional.

  • En caso de finalización excepcional con una CompletionException, métodos get()y get(long, TimeUnit)lanzar una ExecutionExceptioncon la misma causa que llevó a cabo en la correspondiente CompletionException. Para simplificar el uso en la mayoría de contextos, esta clase también define métodos join()y getNow(T)que en vez tirar el CompletionExceptiondirectamente en estos casos.

[...]


Tus preguntas

Aquí está el código de ejemplo:

CompletableFuture.runAsync(() -> {
      List<Integer> integerList = new ArrayList<>();
      integerList.get(1);    // throws exception
    }).thenRun(() -> {
      System.out.println("No exception occurred");
    });

Si usted no es consciente, métodos tales como thenRundevuelven una nueva CompletionStage . Por lo que su código es similar al siguiente:

CompletableFuture<Void> runAsyncStage = CompletableFuture.runAsync(() -> List.of().get(0));
CompletableFuture<Void> thenRunStage =
    runAsyncStage.thenRun(() -> System.out.println("thenRun executing!"));

El thenRunStagese desencadena por la finalización de la runAsyncStagecual, en este caso, está garantizado para completar excepcionalmente con una IndexOutOfBoundsException. En cuanto a la razón por la Runnableque no se ejecuta, eso es debido al contrato de CompletionStage#thenRun(Runnable):

Devuelve una nueva CompletionStageque, cuando esta etapa se completa con normalidad, ejecuta la acción indicada. Consulte la CompletionStagedocumentación de normas que regulan la terminación excepcional.

Por el estado de activación de completar excepcionalmente la thenRunStageetapa también se completa con carácter excepcional, lo que significa que el Runnablese omite.

1. "¿Por qué está fallando en silencio a pesar de que la CompletableFuture completesExceptionally?"

El código de ejemplo es el equivalente de tragar una excepción con un bloque try-catch. No ve la excepción, ya que no tiene código que informar por escrito la excepción. Tanto el runAsyncStagey thenRunStageetapas han completado con carácter excepcional, este último debido a la ex completando excepcional.

Si quieres estar al tanto de la excepción "dentro de la cadena" de etapas entonces usted tiene que etapas de uso, tales como exceptionally[Async], handle[Async], y whenComplete[Async]. Hacerlo de esta manera le permite cambiar el comportamiento de la cadena basándose en la finalización normal o excepcional de una etapa de activación.

Si quieres estar al tanto de la excepción "fuera de la cadena" de etapas a continuación, usted tiene que utilizar métodos tales como join(), get(), y get(long,TimeUnit). Si la etapa había terminado excepcionalmente entonces el primer lanzará una CompletionExceptionenvoltura de la causa de fallo, mientras que los dos últimos se producirá una ExecutionExceptionenvoltura de la causa de fallo.

2. "¿Cómo funciona en el fondo?"

La implementación de CompletableFuturees demasiado compleja para explicar de una respuesta de desbordamiento de pila. Si quieres estudiar la aplicación se puede ver en el código fuente. JDK debería haber venido con un src.ziparchivo que contiene los archivos fuente de Java. También puede mirar el código fuente en línea en los repositorios de OpenJDK . Por ejemplo, aquí está el código fuente del JDK de 13 CompletableFuture:

https://hg.openjdk.java.net/jdk/jdk13/file/0368f3a073a9/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java

3. "¿Es porque estos dos hilos (hilo de rosca del interlocutor e asíncrono) tienen su propio espacio de pila?"

Un hilo no será consciente de una excepción en otro hilo a menos que haya algún tipo de comunicación entre los dos hilos. Métodos de llamada tales como join()voluntad, en su caso, la excepción se comunican a la rosca que se llama tirar dicho excepción. Sin embargo, como lo demuestra la respuesta a la primera pregunta, que es un poco más complicado que eso. Incluso cuando un hilo se produce una excepción dentro de una sola etapa que no verá un seguimiento de pila o algo similar. Esto se debe a que la excepción se atrapó y la etapa se marca como fallido con la excepción de que como la causa . Otro código a continuación, tiene que recuperar de forma explícita y manejar esa excepción, según sea necesario.

Esto no es diferente que el uso de una ExecutorServicey devueltos Futureobjetos. La tarea puede fallar en el fondo, pero otro código no será consciente de ello hasta que el Futurese consulta.

A partir de recompensa: “Estoy buscando entender los detalles de cómo las discusiones interactúan unos con otros.”

No estoy seguro de qué más que añadir. El CompletionStageAPI es una abstracción "por encima de" hilos. Sólo tiene que decir la API de cómo le gustaría que la cadena de comandos para ejecutar, incluyendo los que grupos de subprocesos que se utilizará para cada etapa, y las asas de aplicación toda la comunicación entre hilos para usted. Dicho esto, cada hilo hace su propia cosa, es sólo la API está diseñado para proporcionar una manera más fácil y reactiva para la comunicación entre hilos. Si usted está interesado en la forma en que se implementa entonces te recomiendo estudiar el código fuente (vinculado anteriormente).

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=478175&siteId=1
Recomendado
Clasificación