275 votes

Comment mettre fin à un fil de discussion

Je veux faire fonctionner un fil pendant une durée déterminée. S'il n'est pas terminé dans ce délai, je veux soit le tuer, soit lancer une exception, soit le traiter d'une manière ou d'une autre. Comment cela peut-il être fait ?

Une façon de le faire, comme je l'ai compris à partir de ce fil est d'utiliser une TimerTask dans la méthode run() du Thread.

Existe-t-il de meilleures solutions pour cela ?

 
EDIT : Ajout d'une prime car j'avais besoin d'une réponse plus claire. Le code ExecutorService donné ci-dessous ne répond pas à mon problème. Pourquoi devrais-je dormir() après avoir exécuté (un certain code - je n'ai aucun contrôle sur ce morceau de code) ? Si le code est terminé et que le sleep() est interrompu, comment cela peut-il être un timeOut ?

La tâche qui doit être exécutée n'est pas sous mon contrôle. Il peut s'agir de n'importe quel morceau de code. Le problème, c'est que ce morceau de code peut se retrouver dans une boucle infinie. Je ne veux pas que cela se produise. Donc, je veux juste exécuter cette tâche dans un thread séparé. Le thread parent doit attendre que le thread se termine et doit connaître l'état de la tâche (c'est-à-dire s'il y a eu un dépassement de temps, une exception ou un succès). Si la tâche entre dans une boucle infinie, mon thread parent continue d'attendre indéfiniment, ce qui n'est pas une situation idéale.

0 votes

EDIT : Ajout d'une prime car j'avais besoin d'une réponse plus claire. Le code ExecutorService donné ci-dessous ne répond pas à mon problème. Pourquoi devrais-je sleep() après avoir exécuté mon code ? Si le code est terminé et que le sleep() est interrompu, comment cela peut-il être un timeOut ?

7 votes

Ce sleep() était juste un stub pour représenter "une tâche qui s'exécute depuis longtemps". Remplacez-le simplement par votre vraie tâche ;)

1 votes

... une "tâche de longue haleine" qui se trouve répondre à interrupt() mais ce n'est pas le cas de tous les appels "bloquants", comme j'ai essayé de le souligner dans ma réponse. Les spécificités de la tâche que vous essayez d'interrompre font une énorme différence dans l'approche à utiliser. Plus d'informations sur la tâche seraient utiles.

395voto

BalusC Points 498232

En effet, utilisez plutôt ExecutorService au lieu de Timer voici une SSCCE :

package com.stackoverflow.q2275443;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try {
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
        return "Ready!";
    }
}

Jouez un peu avec le timeout argument dans Future#get() par exemple, augmentez-le à 5 et vous verrez que le fil se termine. Vous pouvez intercepter le délai d'attente dans la méthode catch (TimeoutException e) bloc.

Mise à jour : pour clarifier un malentendu conceptuel, la sleep() es no requis. Il est juste utilisé pour le SSCCE/les démonstrations. Il suffit de faire votre tâche de longue haleine juste là, à la place de sleep() . À l'intérieur de votre tâche de longue durée, vous devriez vérifier si le fil n'est pas [interrompu](http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#interrupted()) comme suit :

while (!Thread.interrupted()) {
    // Do your long running task here.
}

29 votes

Remplacer Thread.sleep(4000) avec une autre déclaration longue durée et l'exemple ne fonctionnera pas. En d'autres termes, cet exemple fonctionnerait seulement si le Task est conçu pour comprendre Thread.isInterrupted() changement de statut.

0 votes

@BalusC J'ai essayé cette approche pour terminer mes fils, mais je n'ai pas réussi à la faire fonctionner. Vous pouvez le vérifier ici : stackoverflow.com/questions/35553420/

0 votes

Comment est gérée l'InterruptedException provoquée par future.cancel(true) ?

52voto

erickson Points 127945

Il n'existe pas de méthode fiable à 100 % pour n'importe quelle tâche. La tâche doit être écrite en tenant compte de cette capacité.

Les bibliothèques Java de base comme ExecutorService annuler les tâches asynchrones avec [interrupt()](http://java.sun.com/javase/6/docs/api/java/lang/Thread.html#interrupt()) sur le fil de travail. Ainsi, par exemple, si la tâche contient une sorte de boucle, vous devez vérifier sa fonction [état des interruptions](http://java.sun.com/javase/6/docs/api/java/lang/Thread.html#interrupted()) à chaque itération. Si la tâche effectue des opérations d'entrée/sortie, elles doivent également être interruptibles, ce qui peut s'avérer délicat à mettre en place. Dans tous les cas, gardez à l'esprit que le code doit vérifier activement la présence d'interruptions ; l'activation d'une interruption n'a pas nécessairement d'effet.

Bien sûr, si votre tâche est une simple boucle, vous pouvez simplement vérifier l'heure actuelle à chaque itération et abandonner lorsqu'un délai spécifié s'est écoulé. Un fil de travail n'est pas nécessaire dans ce cas.

0 votes

D'après mon expérience, le seul code qui ne réagit pas pour commencer à être interrompu est le code natif bloquant (qui attend le système d'exploitation).

0 votes

@ThorbjørnRavnAndersen Je suis d'accord, mais cela fait beaucoup de code. Ce que je veux dire, c'est qu'il n'y a pas de mécanisme universel pour cela ; vous devez comprendre la politique d'interruption de la tâche.

0 votes

@erickson, je suis d'accord avec votre. Pour la réponse au point, il doit y avoir une politique d'annulation définie pour chaque tâche, si vous êtes intéressé à l'arrêter de cette façon. Ou le thread doit être conscient de ce qu'il est censé faire lorsqu'il est interrompu. Après tout, l'interruption et l'arrêt d'un thread n'est qu'une demande que le thread cible peut accepter ou rejeter, il est donc préférable d'écrire une tâche en gardant cela à l'esprit.

13voto

Drew Wills Points 4768

Pensez à utiliser une instance de ExecutorService . Les deux sites invokeAll() y invokeAny() sont disponibles avec un timeout paramètre.

Le thread actuel se bloque jusqu'à ce que la méthode soit terminée (je ne sais pas si c'est souhaitable), soit parce que la ou les tâches se sont terminées normalement, soit parce que le délai d'attente a été atteint. Vous pouvez inspecter les données retournées Future (s) pour déterminer ce qui s'est passé.

10voto

Peter Tseng Points 3272

En supposant que le code du fil est hors de votre contrôle :

De la Java documentation mentionné ci-dessus :

Que faire si un thread ne répond pas à Thread.interrupt ?

Dans certains cas, vous pouvez utiliser des astuces spécifiques à l'application. Par exemple, si un thread est en attente sur une socket connue, vous pouvez fermer la socket pour pour que le thread revienne immédiatement. Malheureusement, il n'y a pas vraiment malheureusement, il n'existe pas de technique qui fonctionne en général. Il convient de noter qu'en toutes les situations dans lesquelles un thread en attente ne répond pas à Thread.interrupt, il ne répondra pas non plus à Thread.stop. Ce site Les cas incluent les attaques délibérées par déni de service, et les opérations d'E/S pour lesquelles thread.stop et thread.interrupt ne fonctionnent pas correctement.

Conclusion :

Assurez-vous que tous les threads peuvent être interrompus, sinon vous devez avoir une connaissance spécifique du thread - comme avoir un drapeau à activer. Peut-être pouvez-vous exiger que la tâche vous soit donnée avec le code nécessaire pour l'arrêter - définissez une interface avec une fonction stop() méthode. Vous pouvez également avertir lorsque vous avez échoué à arrêter une tâche.

8voto

user1310503 Points 72

BalusC a dit :

Mise à jour : pour clarifier un malentendu conceptuel, le sleep() n'est pas nécessaire. Il est juste utilisé pour les besoins de SSCCE/démonstration. Il suffit d'exécuter votre tâche de longue durée à la place de sleep().

Mais si vous remplacez Thread.sleep(4000); con for (int i = 0; i < 5E8; i++) {} alors il ne compile pas, parce que la boucle vide ne lève pas un InterruptedException .

Et pour que le thread puisse être interrompu, il doit lancer une commande InterruptedException .

Cela me semble être un problème sérieux. Je ne vois pas comment adapter cette réponse pour qu'elle fonctionne avec une tâche générale de longue durée.

Modifié pour ajouter : J'ai posé à nouveau cette question comme une nouvelle question : [ Si l'on interrompt un thread après un temps déterminé, doit-il lancer InterruptedException ? ]

0 votes

La façon dont je le fais est d'ajouter un "throws Exception" dans la méthode publique Class<T> call {}.

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