6 votes

Comment tuer un futur Java ?

Le service sur lequel je travaille utilise un Future pour exécuter plusieurs tâches en parallèle ; chaque tâche peut prendre jusqu'à une minute. Cependant, il semble que la librairie externe soit boguée, puisque dans certaines occasions (2% du temps), elle ne revient pas. Dans ces cas-là, je voudrais donner un temps d'attente de 2 minutes, et s'il n'est pas revenu, je voudrais tuer le futur et le reprogrammer plus tard (il finira par réussir).

Comment je tue le futur ?

  private void run() {
    ExecutorService queue = Executors.newFixedThreadPool(1);

    Future<Integer> f = queue.submit(new MyTask());
    Thread.sleep(500);

    try {
      Integer r = f.get(120, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
      e.printStackTrace();
      f.cancel(true);
    }

    // Bad future still running here and I need it dead.

  }

  private class MyTask implements Callable<Integer> {
    private ExternalLibrary extlib = new ExternalLibrary();

    @Override
    public Integer call() throws Exception {
      // step 1 - do a few things

      // step 2 - process data
      Integer val = this.extlib.doSomething(); // here's the problem!

      // step 3 - do other things

      return val;
    }

  }

Je peux voir la librairie externe fonctionner et consommer du CPU (pendant 24 heures)... sans rien faire. C'est une tâche simple qui ne devrait jamais prendre plus de 60 secondes pour accomplir son travail.

Pour l'instant, je tue toute la JVM une fois par jour pour me débarrasser de ce problème, mais je suis sûr qu'il doit y avoir un meilleur moyen. Je me demande comment les serveurs d'applications (Tomcat, JBoss, Weblogic, etc.) font avec les processus rebelles.

3voto

michid Points 3526

Même si vous pouviez tuer le futur suspendu dans la bibliothèque de bogues, cela ne résoudra probablement pas votre problème. La bibliothèque peut encore avoir acquis une ressource qui ne sera pas correctement nettoyée. Il peut s'agir d'allocations de mémoire, de handles de fichiers ouverts ou même de moniteurs laissant certaines structures de données internes dans un état incohérent. Vous finirez probablement par revenir au point où vous devrez redémarrer votre JVM.

Il y a essentiellement deux options : Fixer ou isoler il.

  1. Correction : essayez de faire réparer la bibliothèque. Si cela n'est pas possible,
  2. isoler : isolez la bibliothèque dans un service externe dont dépend votre application. Par exemple, implémentez une API REST pour appeler la bibliothèque et intégrez le tout dans une image Docker. Automatisez le redémarrage du conteneur Docker si nécessaire.

2voto

John Vint Points 19804

Comme d'autres l'ont mentionné, arrêter un Future est coopératif, ce qui signifie que le thread qui s'exécute en asynchrone doit répondre à l'annulation du thread en attente. Si la tâche async n'est pas coopérative, il suffit d'invoquer shutdown ou shutdownNow ne sera pas suffisant car le TPE sous-jacent ne fera que interrupt les fils.

Si vous n'avez aucun contrôle sur extlib et extlib n'est pas coopératif, je vois deux options

  1. Vous pouvez stop le thread en cours d'exécution. Cela peut poser des problèmes si le thread en cours d'arrêt détient un verrou ou une autre ressource. Cela peut conduire à des bogues intéressants qui pourraient être difficiles à disséquer.
  2. Cela pourrait demander un peu plus de travail, mais vous pourriez exécuter la tâche asynchrone en tant que processus distinct. La TPE peut toujours exécuter le processus et, en cas d'interruption, peut destroy le processus. Cela pose évidemment des problèmes plus intéressants, comme la manière de charger le processus avec les données requises.

1voto

Ashish Patil Points 961

Si j'ai bien compris votre demande & en fonction de vos besoins (c'est-à-dire 1 fil), vous pouvez chercher à arrêter executorservice en 2 phases, le code est disponible en doc java du service des exécuteurs :

try {
      Integer r = f.get(120, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
      e.printStackTrace();
      //f.cancel(true);  you can omit this call if you wish. 
      shutdownAndAwaitTermination(queue);
    } ... //remaining method code

void shutdownAndAwaitTermination(ExecutorService pool) {
   pool.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
     }
   } catch (InterruptedException ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }
 }

Veuillez lire la documentation sur shutdown() , shutdownNow() comment ils se comportent car il est clairement mentionné qu'il n'y a pas de garantie à 100% que les tâches / services d'exécution seront arrêtés s'ils sont en cours d'exécution.

0voto

David B.F. Points 51

Dans votre code, l'appel :

this.extlib.doSomething()

doit être synchrone, car si ce n'est pas le cas, le code perd son sens. Avec cette hypothèse, vous pouvez essayer :

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyTask());
try {
    future.get(120, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
} catch (TimeoutException e) {
    future.cancel(true);
} finally {
    executor.shutdownNow();
}

Si cela n'arrête pas le doSomethig c'est parce que cette doSomething est d'ouvrir d'autres fils pour faire le travail. Dans ce cas, vous pouvez peut-être vérifier les threads qui fonctionnent avec :

Thread.getAllStackTraces()

Et essayer de tuer le bon...

0voto

Sachin Points 11

@joe C'est correct. Si vous n'avez pas le contrôle du fil et à l'intérieur du fil, vous ne pouvez pas le tuer.

this.extlib.doSomething();

si cette ligne démarre un fil de discussion, nous devons nous emparer de ce fil pour le tuer car nous n'avons pas de référence pour l'arrêter.

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