Comment tuez-vous un fil en Java?


374

Comment tuez-vous un java.lang.Threaden Java?


2
jusqu'à présent, vous ne pouvez pas tuer un fil; parce que destroy () n'est jamais implémenté en raison d'un verrou
mortel

1
Je préfère la réponse concernant ExecutorStatuscette question: stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Kirby

9
@loungerdork "Je pense que Java devrait implémenter une méthode d'arrêt / destruction sûre pour les threads incontrôlables sur lesquels vous n'avez aucun contrôle, malgré les avertissements de perdre des verrous et d'autres pièges". Vous voulez donc un arrêt de thread dangereux . Je pense que vous en avez déjà un.
DJClayworth

4
Il est étonnant de voir quel genre de questions obtiendrait 212 votes positifs en 2009. Cela serait immédiatement détruit aujourd'hui.
Jonathon Reinhart

7
@JonathonReinhart: Pourquoi cela? Cela semble être une question légitime, même de nos jours. Peut-être que vous ne connaissez pas la frustration que cela provoque lorsque vous avez des threads incontrôlables et ne pouvez utiliser que des fonctions obsolètes pour gérer cela d'une manière ou d'une autre?
TFuto

Réponses:


189

Voir ce fil de Sun sur les raisons de leur dépréciationThread.stop() . Il explique en détail pourquoi il s'agissait d'une mauvaise méthode et ce qui devrait être fait pour arrêter en toute sécurité les threads en général.

La façon dont ils recommandent est d'utiliser une variable partagée comme indicateur qui demande au thread d'arrière-plan de s'arrêter. Cette variable peut ensuite être définie par un autre objet demandant la fin du thread.


1
si vous vérifiez le thread que vous avez interrompu isAlive (), il vous rendra vrai et ils continueront à s'ajouter à votre ThreadGroup [] actuel, vous pouvez le voir en utilisant Thread.currentThread.getThreadGroup (). list (); il imprime tous les threads dont il dispose et vous verrez plusieurs instances de votre thread si vous répétez votre flux.
AZ_

3
Si vous êtes sur un PC, ce n'est pas un problème, mais si vous développez un logiciel pour mobile (Android, je l'ai expérimenté), vous obtiendrez OutOfMemoryError
AZ_

2
Il pourrait / devrait être noté que pour assurer une communication rapide de la demande d'arrêt via un indicateur, la variable doit être volatile (ou l'accès à la variable doit être synchronisé), comme indiqué dans la recommandation.
mtsz

2
Ce lien a été tué à ce stade. J'ai pu le trouver sur archive.org, cependant: web.archive.org/web/20090202093154/http://java.sun.com/j2se/…
Jay Taylor

2
J'utilise la méthode getConnection()de java.sql.DriverManager. Si la connexion prend trop de temps, j'essaie de tuer le thread correspondant en appelant, Thread.interrupt()mais cela n'influence pas du tout le thread. Les Thread.stop()travaux cependant, bien qu'oracle indique que cela ne devrait pas fonctionner sinon interrupt(). Je me demande comment le faire fonctionner et éviter d'utiliser une méthode obsolète.
Danny Lo

129

En général, vous ne le faites pas ..

Vous lui demandez d'interrompre tout ce qu'il fait en utilisant Thread.interrupt () (lien javadoc)

Une bonne explication de pourquoi est dans le javadoc ici (lien technote java)


@Fredrik Qu'arrive-t-il au contexte Thread lorsque la interrupt()méthode est appelée? La question principale est liée à la génération de journaux pour chaque nouveau thread.
ABcDexter

3
@ABcDexter Le fait est que l'interruption n'interrompt rien, elle signale simplement au code du thread (ou au code appelé par le thread) que quelqu'un lui a demandé d'interrompre quoi qu'il fasse. Le thread est ensuite censé arrêter le traitement et revenir, comme s'il avait été fait en faisant ce qu'il devrait (et à ce moment-là, le contexte du thread est probablement également supprimé). OTOH, si vous aviez vraiment forcé l'arrêt du fil, votre question serait vraiment bonne et la réponse indéfinie.
Fredrik

64

En Java, les threads ne sont pas supprimés, mais l'arrêt d'un thread se fait de manière coopérative . Le thread est invité à se terminer et le thread peut ensuite s'arrêter correctement.

Souvent, un volatile booleanchamp est utilisé que le thread vérifie périodiquement et se termine lorsqu'il est défini sur la valeur correspondante.

Je n'utiliserais pas un booleanpour vérifier si le thread doit se terminer . Si vous utilisez volatilecomme modificateur de champ, cela fonctionnera de manière fiable, mais si votre code devient plus complexe, car à la place utilise d'autres méthodes de blocage à l'intérieur de la whileboucle, il peut arriver que votre code ne se termine pas du tout ou du moins prend plus de temps à mesure que vous pourrait vouloir.

Certaines méthodes de bibliothèque de blocage prennent en charge l'interruption.

Chaque thread a déjà un statut d'interruption booléen et vous devez l'utiliser. Il peut être implémenté comme ceci:

public void run() {
   try {
      while (!interrupted()) {
         // ...
      }
   } catch (InterruptedException consumed)
      /* Allow thread to exit */
   }
}

public void cancel() { interrupt(); }

Code source adapté de Java Concurrency in Practice . Puisque la cancel()méthode est publique, vous pouvez laisser un autre thread invoquer cette méthode comme vous le souhaitez.


Et que faire si vous exécutez du code non fiable en tant que plugin ou script? Java a intégré un bac à sable pour le code non approuvé. Et ce bac à sable est inutile, il permet de travailler sans arrêt forcé. Imaginez que vous écrivez un navigateur sur java. La possibilité de tuer un script de page arbitraire est inestimable.
ayvango

@ayvango Ensuite, vous devez exécuter ce script dans votre propre bac à sable. Le bac à sable Java protège la machine de l'application, et non des parties de l'application les unes des autres.
David Schwartz

@DavidSchwartz voulez-vous dire que je devrais simplement utiliser une autre plate-forme que Java?
ayvango

@ayvango Vous pouvez toujours utiliser le sandbox Java pour protéger la machine de votre application. Mais si vous souhaitez protéger des parties de votre application contre d'autres parties de votre application, vous devrez choisir un outil qui peut le faire.
David Schwartz

@DavidSchwartz ASFAIK de tels outils ne pourraient pas exister s'ils ne sont pas pris en charge au niveau de la plate-forme. Mais bien sûr, la tâche pourrait être résolue avec un moteur de script entièrement interprété. Il pourrait compter les réductions comme le fait Erlang et faire d'autres choses.
ayvango

20

Une façon consiste à définir une variable de classe et à l'utiliser comme sentinelle.

Class Outer {
    public static volatile flag = true;

    Outer() {
        new Test().start();
    }
    class Test extends Thread {

        public void run() {
            while (Outer.flag) {
                //do stuff here
            }
        }
    }

}

Définissez une variable de classe externe, c'est-à-dire flag = true dans l'exemple ci-dessus. Réglez-le sur false pour «tuer» le thread.


2
Juste comme un indice: une variable en tant qu'indicateur ne fonctionne que lorsque le thread s'exécute et n'est pas bloqué. Thread.interrupt () devrait libérer le thread de la plupart des conditions d'attente (attente, sommeil, lecture réseau, etc.). Par conséquent, vous ne devez jamais intercepter l'exception InterruptedException pour que cela fonctionne.
René

12
Ce n'est pas fiable; faites un "drapeau" volatilepour vous assurer qu'il fonctionne correctement partout. La classe interne n'est pas statique, l'indicateur doit donc contenir une variable d'instance. L'indicateur doit être effacé dans une méthode d'accesseur afin que d'autres opérations (comme une interruption) puissent être effectuées. Le nom "flag" n'est pas descriptif.
erickson

2
Je ne reçois pas la chose "while" dans la méthode run. Cela ne signifie-t-il pas que tout ce qui est écrit dans la méthode run sera répété? ce n'est pas quelque chose que nous voulions que le fil fasse en premier lieu :(

2
+1 fait pendant que (! Thread.currentThread (). IsInteruppted ()) est préférable
Toby

2
Les deux cas échouent, par exemple lorsque vous ouvrez un processus externe à l'intérieur du processus while {// open ext} et que ce processus est bloqué, maintenant ni le thread ne sera interrompu ni il arrivera à la fin pour vérifier votre condition booléenne, et vous êtes laissé suspendu ... essayez par exemple de lancer une console python en utilisant java.exec et essayez de récupérer le contrôle sans écrire exit, et voyez s'il y a un moyen de tuer ce processus et de sortir .... il n'y a aucun moyen de sortir d'une telle situation ...
Space Rocker

11

Il existe un moyen de le faire. Mais si vous deviez l'utiliser, soit vous êtes un mauvais programmeur, soit vous utilisez un code écrit par de mauvais programmeurs. Donc, vous devriez penser à cesser d'être un mauvais programmeur ou à cesser d'utiliser ce mauvais code. Cette solution est uniquement pour les situations où il n'y a pas d'autre moyen.

Thread f = <A thread to be stopped>
Method m = Thread.class.getDeclaredMethod( "stop0" , new Class[]{Object.class} );
m.setAccessible( true );
m.invoke( f , new ThreadDeath() );

2
Il n'y a aucune raison de le faire, car il est toujours possible d'appeler le public Thread.stopmême s'il est déconseillé.
Lii

1
@Lii Thread.stopfait de même mais vérifie également l'accès et les autorisations. L'utilisation Thread.stopest assez évidente, et je ne me souviens pas de la raison pour laquelle j'ai utilisé à la Thread.stop0place de cela. Peut Thread.stop- être n'a pas fonctionné pour mon cas particulier (Weblogic sur Java 6). Ou peut-être parce qu'il Thread.stopest obsolète et provoque un avertissement.
VadimPlatonov

1
Dans mon scénario, c'était le seul moyen d'arrêter un thread en cours d'exécution sans fin. Pour une raison quelconque .stop () n'a pas arrêté le fil, mais stop0 () l'a fait
fiffy

10

Je voudrais ajouter plusieurs observations, basées sur les commentaires qui se sont accumulés.

  1. Thread.stop() arrêtera un thread si le gestionnaire de sécurité le permet.
  2. Thread.stop()est dangereux. Cela dit, si vous travaillez dans un environnement JEE et que vous n'avez aucun contrôle sur le code appelé, cela peut être nécessaire; voir Pourquoi Thread.stop est déconseillé?
  3. Vous ne devez jamais arrêter d'arrêter un thread de travail de conteneur. Si vous souhaitez exécuter du code qui a tendance à se bloquer, démarrez (avec précaution) un nouveau thread démon et surveillez-le, en le supprimant si nécessaire.
  4. stop()crée une nouvelle ThreadDeathErrorerreur sur le thread appelant , puis renvoie cette erreur sur le thread cible . Par conséquent, la trace de pile est généralement sans valeur.
  5. Dans JRE 6, stop()vérifie auprès du responsable de la sécurité, puis appelle stop1()cet appel stop0(). stop0()est du code natif.
  6. À partir de Java 13 Thread.stop()n'a pas encore été supprimé, mais a Thread.stop(Throwable)été supprimé dans Java 11. ( liste de diffusion , JDK-8204243 )

8

Je voterais pour Thread.stop().

Comme par exemple, vous avez une opération de longue durée (comme une demande de réseau). Soi-disant, vous attendez une réponse, mais cela peut prendre du temps et l'utilisateur a navigué vers une autre interface utilisateur. Ce thread en attente est maintenant a) inutile b) un problème potentiel car lorsqu'il obtiendra un résultat, il sera complètement inutile et il déclenchera des rappels pouvant entraîner un certain nombre d'erreurs.

Tout cela et il peut faire un traitement de réponse qui pourrait être intense en CPU. Et vous, en tant que développeur, ne pouvez même pas l'arrêter, car vous ne pouvez pas lancer de if (Thread.currentThread().isInterrupted())lignes dans tout le code.

Donc, l'incapacité d'arrêter de force un fil est bizarre.


Si l'opération réseau est déjà abandonnée en toute sécurité, interrompez simplement le thread. Si le fonctionnement du réseau n'est pas interrompu en toute sécurité, vous ne pouvez pas appeler de Thread.stop()toute façon en toute sécurité. Vous ne votez pas pour Thread.stop(), vous demandez à chaque personne qui met en œuvre toutes les opérations qui peuvent prendre un certain temps de le rendre sans danger. Et c'est peut-être une bonne idée, mais cela n'a rien à voir avec la mise Thread.stop()en œuvre comme moyen de demander l'avortement sans risque. Nous avons déjà interruptpour ça.
David Schwartz

"Donc, l'incapacité d'arrêter un fil de force, c'est bizarre." - ... jusqu'à ce que vous regardiez profondément (comme les concepteurs Java l'ont fait) et que vous concluez qu'il n'y a pas de solutions techniquement viables qui ne soient pas pires que de déprécier stop.
Stephen C

5

La question est assez vague. Si vous vouliez dire «comment écrire un programme pour qu'un thread cesse de fonctionner quand je le veux», alors diverses autres réponses devraient être utiles. Mais si vous vouliez dire «J'ai une urgence avec un serveur, je ne peux pas redémarrer maintenant et j'ai juste besoin d'un thread particulier pour mourir, quoi qu'il arrive», alors vous avez besoin d'un outil d'intervention pour correspondre aux outils de surveillance comme jstack.

À cet effet, j'ai créé jkillthread . Voir ses instructions d'utilisation.


Je vous remercie! Exactement ce que je cherchais!
Denis Kokorin

4

Il y a bien sûr le cas où vous exécutez une sorte de code pas complètement fiable. (Personnellement, je l'ai en permettant aux scripts téléchargés de s'exécuter dans mon environnement Java. Oui, il y a des sonneries d'alarme de sécurité partout, mais cela fait partie de l'application.) Dans ce cas malheureux, vous ne faites qu'espérer en demandant aux rédacteurs de scripts pour respecter une sorte de signal booléen run / don't-run. Votre seule sécurité intégrée décente consiste à appeler la méthode d'arrêt sur le thread si, par exemple, elle s'exécute plus longtemps qu'un certain délai d'expiration.

Mais, c'est juste "décent", et non absolu, car le code pourrait intercepter l'erreur ThreadDeath (ou toute exception que vous lancez explicitement), et ne pas la renvoyer comme un thread gentleman est censé le faire. Donc, la ligne de fond est AFAIA, il n'y a pas de sécurité absolue.


De plus en plus de services AFAIK deviennent hybrides et utilisent un environnement managé pour exécuter du code tiers (plugin, scripts, etc.) qu'ils ne contrôlent pas entièrement le code, il semble déraisonnable de retirer complètement thread.stop de la table, car pour les ingénieurs de service, un état en direct et en service peut être infiniment meilleur qu'un état sans service (soit en raison d'un blocage (qui enlève les threads), soit d'une boucle infinie occupée (qui enlève les cœurs))
Weipeng L

3

Il n'y a aucun moyen de tuer gracieusement un thread.

Vous pouvez essayer d'interrompre le fil, une stratégie commune consiste à utiliser une pilule empoisonnée pour envoyer un message au fil pour qu'il s'arrête

public class CancelSupport {
    public static class CommandExecutor implements Runnable {
            private BlockingQueue<String> queue;
            public static final String POISON_PILL  = stopnow”;
            public CommandExecutor(BlockingQueue<String> queue) {
                    this.queue=queue;
            }
            @Override
            public void run() {
                    boolean stop=false;
                    while(!stop) {
                            try {
                                    String command=queue.take();
                                    if(POISON_PILL.equals(command)) {
                                            stop=true;
                                    } else {
                                            // do command
                                            System.out.println(command);
                                    }
                            } catch (InterruptedException e) {
                                    stop=true;
                            }
                    }
                    System.out.println(“Stopping execution”);
            }

    }

}

BlockingQueue<String> queue=new LinkedBlockingQueue<String>();
Thread t=new Thread(new CommandExecutor(queue));
queue.put(“hello”);
queue.put(“world”);
t.start();
Thread.sleep(1000);
queue.put(“stopnow”);

http://anandsekar.github.io/cancel-support-for-threads/


2

En règle générale, vous ne tuez pas, n'arrêtez pas ou n'interrompez pas un thread (ou vérifiez s'il est interrompu ()), mais laissez-le se terminer naturellement.

C'est simple. Vous pouvez utiliser n'importe quelle boucle avec une variable booléenne (volatile) à l'intérieur de la méthode run () pour contrôler l'activité du thread. Vous pouvez également revenir du thread actif au thread principal pour l'arrêter.

De cette façon, vous tuez gracieusement un fil :).


1

Les tentatives d'arrêt brutal des threads sont de mauvaises pratiques de programmation bien connues et la preuve d'une mauvaise conception des applications. Tous les threads de l'application multithread partagent explicitement et implicitement le même état de processus et forcés de coopérer les uns avec les autres pour le garder cohérent, sinon votre application sera sujette aux bogues qui seront vraiment difficiles à diagnostiquer. Ainsi, il est de la responsabilité du développeur de fournir une assurance d'une telle cohérence via une conception d'application soignée et claire.

Il existe deux solutions principales pour les terminaisons de threads contrôlées:

  • Utilisation de l'indicateur volatile partagé
  • Utilisation de la paire de méthodes Thread.interrupt () et Thread.interrupted ().

Une bonne explication détaillée des problèmes liés à la terminaison abrupte des threads ainsi que des exemples de solutions erronées et correctes pour la terminaison contrôlée des threads peuvent être trouvés ici:

https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop%28%29+to+terminate+threads


Étant donné que les programmes ne sont plus écrits par un seul développeur, la suppression des threads est souvent nécessaire. Par conséquent, ne peut pas être considéré comme une mauvaise programmation. Je ne peux pas imaginer Linux sans tuer. L'incapacité de tuer les threads est un défaut Java.
Pavel Niedoba

1
Génial, M. Droite ... Et si vous devez appeler une bibliothèque tierce, sur laquelle vous n'avez aucun contrôle et qui a un délai de buggy et peut se bloquer une fois toutes les 1000-2000 fois lors de son exécution? Mauvaise pratique de programmation hein? Eh bien, vous ne pouvez pas toujours avoir accès au code que vous utilisez. Quoi qu'il en soit, l'op demande comment tuer un thread et non comment contrôler son flux lors de la conception de son propre code ...
Arturas M

La question est de savoir ce qui se passera lorsque vous tuerez un thread qui appartient à un mutex ou qui a un bloc de mémoire alloué, ou qui devrait générer un événement ou des données qu'un autre thread attend? Que se passera-t-il avec le reste de la logique de votre application? Vous courrez toujours le risque qu'un petit problème évident se transforme en un problème complexe qui sera difficile à reproduire et à étudier. Tuer le thread n'est pas sûr car il peut laisser votre application dans un certain nombre d'états incohérents différents. Jetez un œil aux informations du lien sur cert.org.
ZarathustrA

Nous avons un fil conducteur qui, à certaines conditions, peut décider que tout le calcul est devenu inutile. Les nombreuses runnables différentes faisant le travail réel doivent être éclaboussées avec une variante de isInterrupted () à n'importe quel endroit pratique - c'est horrible! C'est tellement plus agréable si le contrôleur fait sortir un ThreadDeath de nulle part, déroulant proprement tous les blocs synchronisés et enfin. Tout le monde dit que c'est tellement sujet aux erreurs, mais pour les threads assez indépendants, je ne vois pas pourquoi.
Daniel


1

Je n'ai pas réussi à faire fonctionner l'interruption sur Android, j'ai donc utilisé cette méthode, elle fonctionne parfaitement:

boolean shouldCheckUpdates = true;

private void startupCheckForUpdatesEveryFewSeconds() {
    Thread t = new Thread(new CheckUpdates());
    t.start();
}

private class CheckUpdates implements Runnable{
    public void run() {
        while (shouldCheckUpdates){
            //Thread sleep 3 seconds
            System.out.println("Do your thing here");
        }
    }
}

 public void stop(){
        shouldCheckUpdates = false;
 }

2
le mot-clé volatile doit être ajouté à shouldCheckUpdates, dans le cas où le compilateur optimise avec le stockage local des threads.
clinux

1

«Tuer un fil» n'est pas la bonne expression à utiliser. Voici une façon dont nous pouvons implémenter la fin / sortie gracieuse du fil sur la volonté:

Runnable que j'ai utilisé:

class TaskThread implements Runnable {

    boolean shouldStop;

    public TaskThread(boolean shouldStop) {
        this.shouldStop = shouldStop;
    }

    @Override
    public void run() {

        System.out.println("Thread has started");

        while (!shouldStop) {
            // do something
        }

        System.out.println("Thread has ended");

    }

    public void stop() {
        shouldStop = true;
    }

}

La classe de déclenchement:

public class ThreadStop {

    public static void main(String[] args) {

        System.out.println("Start");

        // Start the thread
        TaskThread task = new TaskThread(false);
        Thread t = new Thread(task);
        t.start();

        // Stop the thread
        task.stop();

        System.out.println("End");

    }

}

le mot-clé volatile doit être ajouté à la variable shouldStop, dans le cas où le compilateur optimise avec le stockage local des threads.
Oleksii Kyslytsyn

0

Thread.stop est obsolète, alors comment arrêter un thread en Java?

Toujours utiliser la méthode d'interruption et l'avenir pour demander l'annulation

  1. Lorsque la tâche répond à un signal d'interruption, par exemple, la méthode de prise de file d'attente de blocage.
Callable < String > callable = new Callable < String > () {
    @Override
    public String call() throws Exception {
        String result = "";
        try {
            //assume below take method is blocked as no work is produced.
            result = queue.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return result;
    }
};
Future future = executor.submit(callable);
try {
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    logger.error("Thread timedout!");
    return "";
} finally {
    //this will call interrupt on queue which will abort the operation.
    //if it completes before time out, it has no side effects
    future.cancel(true);
}
  1. Lorsque la tâche ne répond pas au signal d'interruption.Supposons que la tâche effectue des E / S de socket qui ne répondent pas au signal d'interruption et que l'utilisation de l'approche ci-dessus n'interrompra pas la tâche, l'avenir expirera mais le bloc d'annulation en fin n'aura aucun effet , le fil continuera à écouter le socket. Nous pouvons fermer le socket ou appeler la méthode close à la connexion si elle est implémentée par le pool.
public interface CustomCallable < T > extends Callable < T > {
    void cancel();
    RunnableFuture < T > newTask();
}

public class CustomExecutorPool extends ThreadPoolExecutor {
    protected < T > RunnableFuture < T > newTaskFor(Callable < T > callable) {
        if (callable instanceof CancellableTask)
            return ((CancellableTask < T > ) callable).newTask();
        else
            return super.newTaskFor(callable);
    }
}

public abstract class UnblockingIOTask < T > implements CustomCallable < T > {
    public synchronized void cancel() {
        try {
            obj.close();
        } catch (IOException e) {
            logger.error("io exception", e);
        }
    }

    public RunnableFuture < T > newTask() {
        return new FutureTask < T > (this) {
            public boolean cancel(boolean mayInterruptIfRunning) {
                try {
                    this.cancel();
                } finally {
                    return super.cancel(mayInterruptIfRunning);
                }
            }

        };
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.