try tratar propagacion programa personalizadas manejo lista excepciones catch java multithreading scheduling

java - tratar - ScheduledExecutorService Manejo de excepciones



propagacion de excepciones en java (6)

tl; dr

Cualquier excepción que escape de su método de run detiene cualquier trabajo adicional, sin previo aviso.

Siempre use un try-catch dentro de su método de run . Intenta recuperarte si quieres que la actividad programada continúe.

@Override public void run () { try { doChore(); } catch ( Exception e ) { logger.error( "Caught exception in ScheduledExecutorService. StackTrace:/n" + t.getStackTrace() ); } }

El problema

La pregunta se refiere al truco crítico con un ScheduledExecutorService : cualquier excepción o error arrojado al ejecutor hace que el ejecutor se detenga. No más invocaciones en el Runnable, no más trabajo realizado. Este paro de trabajo ocurre en silencio, usted no será informado. Esta publicación de blog en lenguaje travieso narra de manera entretenida la manera difícil de aprender sobre este comportamiento.

La solución

La respuesta de yegor256 y la respuesta de arun_suresh parecen ser básicamente correctas. Dos problemas con esas respuestas:

  • Errores de captura así como excepciones.
  • Un poco complicado

¿Errores y excepciones?

En Java normalmente solo detectamos exceptions , no errors . Pero en este caso especial de ScheduledExecutorService, fallar en cualquiera de los dos significará un paro laboral. Así que es posible que desee atrapar a ambos. No estoy 100% seguro de esto, sin saber completamente las implicaciones de capturar todos los errores. Por favor corrígeme si es necesario.

Una forma de detectar tanto las excepciones como los errores es capturar su superclase, Throwable .

} catch ( Throwable t ) {

…más bien que…

} catch ( Exception e ) {

Enfoque más simple: simplemente agregue un Try-Catch

Pero ambas respuestas son un poco complicadas. Solo para que conste, te mostraré la solución más simple:

Envuelva siempre el código de su Runnable en un Try-Catch para detectar cualquiera y todas las excepciones y errores.

Sintaxis lambda

Con una lambda (en Java 8 y posteriores).

final Runnable someChoreRunnable = () -> { try { doChore(); } catch ( Throwable t ) { // Catch Throwable rather than Exception (a subclass). logger.error( "Caught exception in ScheduledExecutorService. StackTrace:/n" + t.getStackTrace() ); } };

Sintaxis pasada de moda

La manera pasada de moda, antes de las lambdas.

final Runnable someChoreRunnable = new Runnable() { @Override public void run () { try { doChore(); } catch ( Throwable t ) { // Catch Throwable rather than Exception (a subclass). logger.error( "Caught exception in ScheduledExecutorService. StackTrace:/n" + t.getStackTrace() ); } } };

En cada Runnable / Callable

Independientemente de un ScheduledExecutorService , me parece sensato utilizar siempre un try-catch( Exception† e ) general try-catch( Exception† e ) en cualquier método de run de un Runnable . Lo mismo ocurre con cualquier método de call de un Callable .

Código de ejemplo completo

En el trabajo real, probablemente definiría Runnable separado en lugar de anidado. Pero esto lo convierte en un excelente ejemplo todo en uno.

package com.basilbourque.example; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** * Demo `ScheduledExecutorService` */ public class App { public static void main ( String[] args ) { App app = new App(); app.doIt(); } private void doIt () { // Demonstrate a working scheduled executor service. // Run, and watch the console for 20 seconds. System.out.println( "BASIL - Start." ); ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture < ? > handle = scheduler.scheduleWithFixedDelay( new Runnable() { public void run () { try { // doChore ; // Do business logic. System.out.println( "Now: " + ZonedDateTime.now( ZoneId.systemDefault() ) ); // Report current moment. } catch ( Exception e ) { // … handle exception/error. Trap any unexpected exception here rather to stop it reaching and shutting-down the scheduled executor service. // logger.error( "Caught exception in ScheduledExecutorService. StackTrace:/n" + e.getStackTrace() ); } // End of try-catch. } // End of `run` method. } , 0 , 2 , TimeUnit.SECONDS ); // Wait a long moment, for background thread to do some work. try { Thread.sleep( TimeUnit.SECONDS.toMillis( 20 ) ); } catch ( InterruptedException e ) { e.printStackTrace(); } // Time is up. Kill the executor service and its thread pool. scheduler.shutdown(); System.out.println( "BASIL - Done." ); } }

Cuando se ejecuta

ALBAHACA - Inicio.

Ahora: 2018-04-10T16: 46: 01.423286-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 03.449178-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 05.450107-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 07.450586-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 09.456076-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 11.456872-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 13.461944-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 15.463837-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 17.469218-07: 00 [América / Los_Angeles]

Ahora: 2018-04-10T16: 46: 19.473935-07: 00 [América / Los_Angeles]

ALBAHACA - Hecho.

† O quizás Throwable lugar de exceptions para capturar objetos de errors también.

Utilizo ScheduledExecutorService para ejecutar un método periódicamente.

código p:

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture<?> handle = scheduler.scheduleWithFixedDelay(new Runnable() { public void run() { //Do business logic, may Exception occurs } }, 1, 10, TimeUnit.SECONDS);

Mi pregunta:

¿Cómo continuar el planificador, si run() lanza Excepción? ¿Debería intentar capturar todas las excepciones en el método run() ? ¿O cualquier método de devolución de llamada incorporado para manejar la excepción? ¡Gracias!


Cualquier excepción en la ejecución () de un hilo que se pasa a (ScheduledExecutorService) nunca se desecha y si usamos future.get () para obtener el estado, el hilo principal espera infinitamente


Debe usar el objeto ScheduledFuture devuelto por su scheduler.scheduleWithFixedDelay(...) así:

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture<?> handle = scheduler.scheduleWithFixedDelay(new Runnable() { public void run() { throw new RuntimeException("foo"); } }, 1, 10, TimeUnit.SECONDS); // Create and Start an exception handler thread // pass the "handle" object to the thread // Inside the handler thread do : .... try { handle.get(); } catch (ExecutionException e) { Exception rootException = e.getCause(); }


Inspirado en la solución @MBec, escribí un bonito contenedor genérico para el ScheduledExecutorService que:

  • capturará e imprimirá cualquier excepción lanzada no manejada.
  • devolverá un Java 8 CompletableFuture en lugar de un futuro.

:)

import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * This class use as a wrapper for the Native Java ScheduledExecutorService class. * It was created in order to address the very unpleasant scenario of silent death! * explanation: each time an unhandled exception get thrown from a running task that runs by ScheduledExecutorService * the thread will die and the exception will die with it (nothing will propagate back to the main thread). * * However, HonestScheduledExecutorService will gracefully print the thrown exception with a custom/default message, * and will also return a Java 8 compliant CompletableFuture for your convenience :) */ @Slf4j public class HonestScheduledExecutorService { private final ScheduledExecutorService scheduledExecutorService; private static final String DEFAULT_FAILURE_MSG = "Failure occurred when running scheduled task."; HonestScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) { this.scheduledExecutorService = scheduledExecutorService; } public CompletableFuture<Object> scheduleWithFixedDelay(Callable callable, String onFailureMsg, long initialDelay, long delay, TimeUnit unit) { final String msg = StringUtils.isEmpty(onFailureMsg) ? DEFAULT_FAILURE_MSG : onFailureMsg; CompletableFuture<Object> delayed = new CompletableFuture<>(); scheduledExecutorService.scheduleWithFixedDelay(() -> { try { Object result = callable.call(); delayed.complete(result); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }, initialDelay, delay, unit); return delayed; } public CompletableFuture<Void> scheduleWithFixedDelay(Runnable runnable, String onFailureMsg, long initialDelay, long delay, TimeUnit unit) { final String msg = StringUtils.isEmpty(onFailureMsg) ? DEFAULT_FAILURE_MSG : onFailureMsg; CompletableFuture<Void> delayed = new CompletableFuture<>(); scheduledExecutorService.scheduleWithFixedDelay(() -> { try { runnable.run(); delayed.complete(null); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }, initialDelay, delay, unit); return delayed; } public CompletableFuture<Object> schedule(Callable callable, String failureMsg, long delay, TimeUnit unit) { final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg; CompletableFuture<Object> delayed = new CompletableFuture<>(); scheduledExecutorService.schedule(() -> { try { Object result = callable.call(); delayed.complete(result); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }, delay, unit); return delayed; } public CompletableFuture<Void> schedule(Runnable runnable, String failureMsg, long delay, TimeUnit unit) { final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg; CompletableFuture<Void> delayed = new CompletableFuture<>(); scheduledExecutorService.schedule(() -> { try { runnable.run(); delayed.complete(null); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }, delay, unit); return delayed; } public CompletableFuture<Object> scheduleAtFixedRate(Callable callable, String failureMsg, long initialDelay, long period, TimeUnit unit) { final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg; CompletableFuture<Object> delayed = new CompletableFuture<>(); scheduledExecutorService.scheduleAtFixedRate(() -> { try { Object result = callable.call(); delayed.complete(result); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }, initialDelay, period, unit); return delayed; } public CompletableFuture<Void> scheduleAtFixedRate(Runnable runnable, String failureMsg, long initialDelay, long period, TimeUnit unit) { final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg; CompletableFuture<Void> delayed = new CompletableFuture<>(); scheduledExecutorService.scheduleAtFixedRate(() -> { try { runnable.run(); delayed.complete(null); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }, initialDelay, period, unit); return delayed; } public CompletableFuture<Object> execute(Callable callable, String failureMsg) { final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg; CompletableFuture<Object> delayed = new CompletableFuture<>(); scheduledExecutorService.execute(() -> { try { Object result = callable.call(); delayed.complete(result); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }); return delayed; } public CompletableFuture<Void> execute(Runnable runnable, String failureMsg) { final String msg = StringUtils.isEmpty(failureMsg) ? DEFAULT_FAILURE_MSG : failureMsg; CompletableFuture<Void> delayed = new CompletableFuture<>(); scheduledExecutorService.execute(() -> { try { runnable.run(); delayed.complete(null); } catch (Throwable th) { log.error(msg, th); delayed.completeExceptionally(th); } }); return delayed; } public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return scheduledExecutorService.awaitTermination(timeout, unit); } public List<Runnable> shutdownNow() { return scheduledExecutorService.shutdownNow(); } public void shutdown() { scheduledExecutorService.shutdown(); } }


Otra solución sería tragar una excepción en el Runnable . Puede usar una clase conveniente de VerboseRunnable de jcabi-log , por ejemplo:

import com.jcabi.log.VerboseRunnable; scheduler.scheduleWithFixedDelay( new VerboseRunnable( Runnable() { public void run() { // do business logic, may Exception occurs } }, true // it means that all exceptions will be swallowed and logged ), 1, 10, TimeUnit.SECONDS );


Sé que esta es una pregunta antigua, pero si alguien está utilizando CompletableFuture con ScheduledExecutorService retrasado, entonces debería manejar esto de esa manera:

private static CompletableFuture<String> delayed(Duration delay) { CompletableFuture<String> delayed = new CompletableFuture<>(); executor.schedule(() -> { String value = null; try { value = mayThrowExceptionOrValue(); } catch (Throwable ex) { delayed.completeExceptionally(ex); } if (!delayed.isCompletedExceptionally()) { delayed.complete(value); } }, delay.toMillis(), TimeUnit.MILLISECONDS); return delayed; }

y manejo de excepción en CompletableFuture :

CompletableFuture<String> delayed = delayed(Duration.ofSeconds(5)); delayed.exceptionally(ex -> { //handle exception return null; }).thenAccept(value -> { //handle value });