web-dev-qa-db-fra.com

Comment démarrer/arrêter/redémarrer un thread en Java?

J'ai beaucoup de mal à trouver un moyen de démarrer, d'arrêter et de redémarrer un fil de discussion en Java.

Plus précisément, j'ai une classe Task (implémente actuellement Runnable) dans un fichier Task.Java. Mon application principale doit pouvoir démarrer cette tâche sur un fil, arrêter le fil quand il le faut, et parfois tuer et relancer le fil ...

Ma première tentative a été avec ExecutorService mais je n'arrive pas à trouver le moyen de le relancer. Lorsque j'utilise .shutdownnow(), tout appel futur à .execute() échoue car la ExecutorService est "arrêt" ...

Alors, comment pourrais-je accomplir cela?

47
Shaitan00

Une fois qu'un thread s'est arrêté, vous ne pouvez plus le redémarrer. Cependant, rien ne vous empêche de créer et de démarrer un nouveau thread.

Option 1: Créez un nouveau thread plutôt que d'essayer de redémarrer.

Option 2: Au lieu de laisser le thread s'arrêter, faites-le attendre, puis, lorsqu'il recevra une notification, vous pourrez le laisser fonctionner à nouveau. De cette façon, le thread ne s'arrête jamais et n'aura jamais besoin d'être redémarré.

Édition basée sur un commentaire:

Pour "tuer" le fil, vous pouvez faire quelque chose comme ce qui suit.

yourThread.setIsTerminating(true); // tell the thread to stop
yourThread.join(); // wait for the thread to stop
41
Taylor Leese

Il est impossible de terminer un thread à moins que le code s'exécutant dans ce thread ne vérifie et autorise la résiliation.

Vous avez dit: "Malheureusement, je dois le tuer/le redémarrer ... Je n'ai pas le contrôle total sur le contenu du fil et, dans ma situation, il nécessite un redémarrage"

Si le contenu du fil ne permet pas de mettre fin à son exécution, vous ne pouvez pas mettre fin à ce fil.

Dans votre message, vous dites: "Ma première tentative a été avec ExecutorService mais je n'arrive pas à trouver le moyen de le relancer. Lorsque j'utilise .shutdownnow () ..."

Si vous regardez la source de "shutdownnow", elle traverse et interrompt les threads en cours d'exécution. Cela n'arrêtera pas leur exécution à moins que le code de ces threads vérifie s'il a été inerrupted et, le cas échéant, arrête l'exécution elle-même. Donc, shutdownnow ne fait probablement pas ce que vous pensez.

Laissez-moi illustrer ce que je veux dire quand je dis que le contenu du fil doit permettre de mettre fin à ce fil:

myExecutor.execute(new Runnable() {
 public void run() {
  while (true) {
    System.out.println("running");
  }
 }
 });
myExecutor.shutdownnow();

Ce thread continuera à fonctionner pour toujours, même si shutdownnow a été appelé, car il ne vérifie jamais s'il a été arrêté ou non. Ce fil, cependant, va s'arrêter:

myExecutor.execute(new Runnable() {
 public void run() {
  while (!Thread.interrupted()) {
    System.out.println("running");
  }
 }
 });
myExecutor.shutdownnow();

Depuis ce thread vérifie s'il a été interrompu/arrêté/terminé.

Donc, si vous voulez un thread que vous pouvez fermer, vous devez vous assurer qu'il vérifie s'il a été interrompu. Si vous voulez un thread que vous pouvez "arrêter" et "redémarrer", vous pouvez créer un exécutable qui peut prendre de nouvelles tâches, comme cela a été mentionné auparavant.

Pourquoi ne pouvez-vous pas arrêter un thread en cours d'exécution? Eh bien, j'ai menti, vous pouvez appeler "yourThread.stop ()" mais pourquoi est-ce une mauvaise idée? Le thread pourrait se trouver dans une section de code synchronisée (ou dans une autre section critique, mais nous nous limiterons aux séquences gardées par la clé synchronisée ici) lorsque vous l'arrêtez. Les blocs de synchronisation sont supposés être exécutés dans leur intégralité et uniquement par un thread avant d'être accédés par un autre thread. Si vous arrêtez un thread au milieu d'un bloc de synchronisation, la protection mise en place par le bloc de synchronisation est invalidée et votre programme entrera dans un état inconnu. Les développeurs fabriquent des éléments dans des blocs synchronisés pour rester synchronisés. Si vous utilisez threadInstance.stop (), vous détruisez le sens de synchronize, ce que le développeur de ce code essayait d'accomplir et comment le développeur de ce code s'attendait à ce que ses blocs synchronisés se comporter.

9
Matt Crinklaw-Vogt

Passez en revue Java.lang.Thread .

Pour démarrer ou redémarrer (une fois qu'un thread est arrêté, vous ne pouvez pas redémarrer le même thread, mais cela n'a pas d'importance; créez simplement une nouvelle instance Thread):

// Create your Runnable instance
Task task = new Task(...);

// Start a thread and run your Runnable
Thread t = new Thread(task);

Pour l'arrêter, utilisez une méthode sur votre instance Task qui définit un indicateur pour indiquer à la méthode run de quitter; Si vous revenez de run, vous quittez le thread. Si votre code d'appel a besoin de savoir que le fil s'est vraiment arrêté avant son retour, vous pouvez utiliser join:

// Tell Task to stop
task.setStopFlag(true);

// Wait for it to do so
t.join();

Concernant le redémarrage: Même si une Thread ne peut pas être redémarrée, vous pouvez réutiliser votre instance Runnable avec un nouveau thread si elle a l’état que vous souhaitez conserver; cela revient au même. Assurez-vous simplement que votre Runnable est conçue pour permettre plusieurs appels à run.

8
T.J. Crowder

Vous ne pouvez pas redémarrer un thread; votre meilleure option consiste donc à enregistrer l'état actuel de l'objet au moment de l'arrêt du thread et lorsque les opérations doivent continuer sur cet objet. Vous pouvez recréer cet objet à l'aide de la sauvegarde puis démarrer le nouveau thread .

Ces deux articles Swing Worker et Concurrence peuvent vous aider à déterminer la meilleure solution à votre problème.

4
ChadNC

Comme l'a déclaré Taylor L, vous ne pouvez pas simplement "arrêter" un thread (en appelant une méthode simple) car il pourrait laisser votre système dans un état instable car le thread d'appel externe peut ne pas savoir ce qui se passe à l'intérieur. votre fil.

Cela dit, le meilleur moyen de "stopper" un fil est de le laisser garder un œil sur lui-même et de le faire savoir et comprendre quand il doit s'arrêter.

2
JasCav
You can start a thread like:

    Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //Do you task
                    }catch (Exception ex){
                        ex.printStackTrace();}
                }
            });
thread.start();

To stop a Thread:

   thread.join();//it will kill you thread

   //if you want to know whether your thread is alive or dead you can use
System.out.println("Thread is "+thread.isAlive());

Il est conseillé de créer un nouveau thread plutôt que de le redémarrer.

0
Sukirti Dash

Il y a une différence entre mettre un fil en pause et l'arrêter/le tuer. Si vous vous arrêtez pour tuer le thread, un redémarrage signifie simplement créer un nouveau thread et le lancer.

Il existe des méthodes pour supprimer les threads d'un autre thread (votre géniteur, par exemple), mais elles sont généralement peu sûres. Cela pourrait être plus sûr si votre thread vérifie constamment un indicateur pour voir s'il doit continuer (je suppose qu'il y a une boucle dans votre thread) et que le "contrôleur" externe modifie l'état de cet indicateur. 

Vous en verrez un peu plus dans: http://Java.Sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html

Puis-je vous demander pourquoi vous voulez tuer le fil et le redémarrer? Pourquoi ne pas simplement le laisser attendre que ses services soient de nouveau nécessaires? Java a des mécanismes de synchronisation exactement à cette fin. Le thread restera en veille jusqu'à ce que le contrôleur lui notifie de poursuivre son exécution.

0
Uri

Si votre tâche exécute une sorte d'action dans une boucle, il existe un moyen de suspendre/redémarrer le traitement, mais je pense que cela devrait être en dehors de ce que propose actuellement l'API Thread. S'il s'agit d'un processus unique, je ne connais aucun moyen de suspendre/redémarrer sans exécuter une API obsolète ou interdite.

En ce qui concerne les processus en boucle, la façon la plus simple à laquelle je puisse penser est que le code qui génère la tâche instancie un objet ReentrantLock et le transfère à la tâche, tout en conservant lui-même une référence. Chaque fois que la tâche entre dans sa boucle, elle tente de verrouiller l'instance de ReentrantLock et, une fois celle-ci terminée, elle doit être déverrouillée. Vous voudrez peut-être encapsuler tout cela au final, en vous assurant que vous lâchez le verrou à la fin de la boucle, même si une exception est levée.

Si vous souhaitez suspendre la tâche, essayez simplement de verrouiller le code principal (car vous avez gardé une référence à portée de main). Ce que cela va faire, c'est attendre que la boucle se termine et ne pas le laisser démarrer une autre itération (puisque le thread principal détient un verrou). Pour redémarrer le thread, déverrouillez simplement le code principal, cela permettra à la tâche de reprendre ses boucles.

Pour arrêter définitivement le fil, j'utilisais l'API normale ou laissais un indicateur dans la tâche et un attribut pour l'indicateur (quelque chose comme stopImmediately). Lorsque la boucle rencontre une valeur vraie pour cet indicateur, elle arrête le traitement et termine la méthode d'exécution.

0
Gennadiy