36 votes

Meilleures pratiques de Java Executor pour les tâches à exécuter pour toujours

Je suis en train de travailler sur un projet en Java où j'ai besoin d'avoir plusieurs tâches en cours d'exécution asynchrone. Je suis amené à croire que l'Exécuteur testamentaire est la meilleure façon pour moi de le faire, donc je vais me familiariser avec elle. (Yay payé pour le savoir!) Cependant, il n'est pas clair pour moi ce que le meilleur moyen est d'accomplir ce que je suis en train de faire.

Pour les besoins de la discussion, disons que j'ai deux tâches en cours d'exécution. Ni devrait mettre fin, et les deux doivent s'exécuter pendant la durée de la vie. Je suis en train d'écrire l'une des principales classe wrapper tels que:

  • Si l'une des tâches lève une exception, le wrapper de l'attraper et de le redémarrer la tâche.
  • Si la tâche s'exécute à la fin, le wrapper avis et redémarrer la tâche.

Maintenant, il convient de noter que la mise en œuvre pour les deux tâches de placer le code en run() dans une boucle infinie qui ne sera jamais exécuté jusqu'à la fin, avec un bloc try/catch qui doit gérer toutes les exceptions d'exécution sans perturber la boucle. Je suis en train d'ajouter une autre couche de certitude; si j'ai quelqu'un qui me suit fait quelque chose de stupide que les défaites de ces garanties et arrêts de la tâche, l'application doit réagir de façon appropriée.

Est-il une bonne pratique pour gérer ce problème, que des gens plus expérimentés que me recommanderiez?

FWIW, je l'ai fouettée à cette classe de test:


public class ExecTest {

   private static ExecutorService executor = null;
   private static Future results1 = null;
   private static Future results2 = null;

   public static void main(String[] args) {
      executor = Executors.newFixedThreadPool(2);
      while(true) {
         try {
            checkTasks();
            Thread.sleep(1000);
         }
         catch (Exception e) {
            System.err.println("Caught exception: " + e.getMessage());
         }
      }
   }

   private static void checkTasks() throws Exception{
      if (results1 == null || results1.isDone() || results1.isCancelled()) {
         results1 = executor.submit(new Test1());
      }

      if (results2 == null || results2.isDone() || results2.isCancelled()) {
         results2 = executor.submit(new Test2());
      }
   }
}

class Test1 implements Runnable {
   public void run() {
      while(true) {
         System.out.println("I'm test class 1");
         try {Thread.sleep(1000);} catch (Exception e) {}
      }

   }
}

class Test2 implements Runnable {
   public void run() {
      while(true) {
         System.out.println("I'm test class 2");
         try {Thread.sleep(1000);} catch (Exception e) {}
      }
   }
}

C'est se comporter de la façon dont je le veux, mais je ne sais pas s'il y a des pièges, de l'inefficacité, ou carrément mal de tête d'attente à me surprendre. (En fait, étant donné que je suis nouveau à cela, je serais choqué si il y n'était pas quelque chose de mal/déconseillé à ce sujet.)

Toute idée est la bienvenue.

31voto

Yoni Points 5806

J'ai fait face à une situation similaire dans mon précédent projet, et d'après mon code a soufflé dans le visage d'un client mécontent, mes copains et j'ai ajouté deux grand-fort-gardes:

  1. Dans la boucle infinie, de déceler les Erreurs, pas juste des exceptions. Parfois unexcepted les choses et Java lève une Erreur à vous, et non pas une Exception.
  2. L'utilisation d'un back-off de l'interrupteur, de sorte que si quelque chose va mal et n'est pas récupérable, vous n'avez pas aggraver la situation en impatiemment de départ d'une autre boucle. Au lieu de cela, vous devez attendre jusqu'à ce que la situation va revenir à la normale et puis recommencer.

Par exemple, nous avons eu une situation où la base de données est allé vers le bas et pendant la boucle d'une SQLException est levée. Le résultat fâcheux est que le code est allé à travers la boucle, encore une fois, seulement pour atteindre la même exception à nouveau, et ainsi de suite. Les journaux ont montré que nous avons touché le même SQLException environ 300 fois en une seconde!! ... ce qui s'est passé de façon intermittente à plusieurs reprises avec occassional JVM des pauses de 5 secondes ou si, au cours de laquelle la demande n'a pas été sensible, jusqu'à ce que finalement une Erreur a été générée et le thread est mort!

Nous avons donc mis en place un back-off de la stratégie, environ indiqué dans le code ci-dessous, que si l'exception n'est pas récupérable (ou est l'exception à récupérer, dans une affaire de minutes), puis nous attendre plus de temps avant de reprendre ses activités.

class Test1 implements Runnable {
  public void run() {
    boolean backoff = false;
    while(true) {
      if (backoff) {
        Thread.sleep (TIME_FOR_LONGER_BREAK);
        backoff = false;
      }
      System.out.println("I'm test class 1");
      try {
        // do important stuff here, use database and other critical resources
      }
      catch (SqlException se) {
       // code to delay the next loop
       backoff = true;
      }
      catch (Exception e) {
      }
      catch (Throwable t) {
      }
    }
  }
}

Si vous mettez en œuvre vos tâches de cette façon, alors je ne vois pas l'intérêt d'avoir une troisième "watch-dog" thread avec la checkTasks() la méthode. En outre, pour les mêmes raisons que j'ai décrites ci-dessus, je serais prudent de commencer à nouveau la tâche avec l'exécuteur testamentaire. Vous devez d'abord comprendre pourquoi la tâche a échoué et que l'environnement est dans un état stable que l'exécution de la tâche à nouveau serait utile.

7voto

David Sowsy Points 1416

De côté pour eyeballing, j'généralement exécuter du code Java contre les outils d'analyse statique comme PMD et FindBugs de regarder pour des questions plus profondes.

Spécifiquement pour ce code FindBugs n'aimait pas que exploitation1 et results2 ne sont pas volatiles dans les paresseux init, et que le run() méthodes peut ignorer l'Exception, car ils ne sont pas explicitement traitées.

En général, je suis un peu réticent à l'idée de l'utilisation de Thread.dormir à la simultanéité des tests, préférant minuteries ou de la cessation de membres/conditions. Callable pourrait être utile dans le retour de quelque chose dans l'éventualité d'une perturbation qui lève une exception en cas d'impossibilité de calculer un résultat.

Pour certains, les meilleures pratiques et plus de nourriture pour la pensée, découvrez la Simultanéité dans la Pratique.

0voto

avez-vous essayé le framework Quartz ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X