Tl;dr
Toute exception échappant à votre run
méthode arrête tout travail ultérieur sans préavis.
Utilisez toujours un try-catch
au sein de votre run
méthode. Essayez de récupérer si vous voulez que l'activité programmée continue.
@Override
public void run ()
{
try {
doChore();
} catch ( Exception e ) {
logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
}
}
Le problème
La question fait référence à l'astuce critique avec un ScheduledExecutorService
: Toute exception ou erreur lancée atteignant l'exécuteur provoque l'arrêt de ce dernier. Plus d'invocations sur le Runnable, plus de travail effectué. Cet arrêt de travail se produit silencieusement, vous n'en serez pas informé. Ce vilain langage affichage du blog raconte de façon divertissante la manière difficile d'apprendre ce comportement.
La solution
El réponse de yegor256 y el réponse de arun_suresh Les deux semblent être fondamentalement correctes. Deux problèmes avec ces réponses :
- Attraper les erreurs ainsi que les exceptions
- Un peu compliqué
Erreurs y Exceptions ?
En Java, nous n'attrapons normalement que exceptions pas erreurs . Mais dans ce cas particulier de ScheduledExecutorService, ne pas attraper l'un ou l'autre signifiera un arrêt de travail. Vous pouvez donc vouloir attraper les deux. Je ne suis pas sûr à 100% de cela, ne connaissant pas toutes les implications de la capture de toutes les erreurs. Veuillez me corriger si nécessaire.
Une des raisons pour lesquelles il faut attraper les erreurs et les exceptions peut impliquer l'utilisation de bibliothèques dans votre tâche. Voir le commentaire de jannis .
Une façon d'attraper à la fois les exceptions et les erreurs est d'attraper leur superclasse, Jetable pour un exemple.
} catch ( Throwable t ) {
plutôt que
} catch ( Exception e ) {
L'approche la plus simple : Il suffit d'ajouter un Try-Catch
Mais les deux réponses sont un peu compliquées. Pour mémoire, je vais montrer la solution la plus simple :
Enveloppez toujours le code de votre Runnable dans un Try-Catch pour attraper toutes les exceptions. y erreurs.
Syntaxe Lambda
Avec un lambda (en Java 8 et plus).
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() );
}
};
Syntaxe à l'ancienne
A l'ancienne, avant les 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() );
}
}
};
Dans chaque Runnable/Callable
Indépendamment d'un ScheduledExecutorService
il me semble judicieux de toujours utiliser une formule générale de try-catch( Exception† e )
sur tout run
méthode d'un Runnable
. Idem pour tout call
méthode d'un Callable
.
Exemple complet de code
Dans un travail réel, je définirais probablement la Runnable
séparément plutôt qu'imbriqués. Mais cela permet de faire un bel exemple tout-en-un.
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." );
}
}
Quand il est exécuté.
BASIL - Démarrage.
Now: 2018-04-10T16:46:01.423286-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:03.449178-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:05.450107-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:07.450586-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:09.456076-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:11.456872-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:13.461944-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:15.463837-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:17.469218-07:00[America/Los_Angeles]
Now: 2018-04-10T16:46:19.473935-07:00[America/Los_Angeles]
BASIL - Fait.
† Ou peut-être Throwable
au lieu de Exception
pour attraper Error
également des objets.