web-dev-qa-db-fra.com

Gestion des exceptions ScheduledExecutorService

J'utilise ScheduledExecutorService pour exécuter une méthode périodiquement.

p-code:

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

Ma question:

Comment continuer le planificateur, si run() lève une exception? Dois-je essayer d'attraper toutes les exceptions dans la méthode run()? Ou toute méthode de rappel intégrée pour gérer l'exception? Merci!

44

Vous devriez utiliser l'objet ScheduledFuture retourné par votre scheduler.scheduleWithFixedDelay(...) comme ceci:

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();
}
32
arun_suresh

Je sais que c’est une vieille question, mais si quelqu'un utilise le CompletableFuture avec ScheduledExecutorService en différé, il devrait alors traiter cela de la manière suivante:

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;
}

et gestion de l'exception dans CompletableFuture:

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

Une autre solution consisterait à avaler une exception dans la variable Runnable. Vous pouvez utiliser une commode VerboseRunnable class à partir de jcabi-log , par exemple:

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
);
3
yegor256

Inspiré par la solution @MBec, j'ai écrit un wrapper générique Nice pour ScheduledExecutorService qui:

  • capturera et imprimera toute exception levée non gérée.
  • renverra un Java 8 CompletableFuture au lieu d'un futur.

:)

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();
    }

}
2
dorony

Toute exception dans le run () d'un thread qui est passé à (ScheduledExecutorService) n'est jamais rejetée et si nous utilisons future.get () pour obtenir le statut, le thread principal attend indéfiniment 

0
Guru