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!
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();
}
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
});
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
);
Inspiré par la solution @MBec, j'ai écrit un wrapper générique Nice pour ScheduledExecutorService qui:
:)
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();
}
}
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