Est-il légal d'appeler la méthode start deux fois sur le même thread?

89

Le code suivant mène au java.lang.IllegalThreadStateException: Thread already startedmoment où j'ai appelé la start()méthode une deuxième fois dans le programme.

updateUI.join();    

if (!updateUI.isAlive()) 
    updateUI.start();

Cela se produit la deuxième fois updateUI.start()est appelée. Je l'ai parcouru plusieurs fois et le thread est appelé et se termine complètement avant de frapper updateUI.start().

L'appel updateUI.run()évite l'erreur mais provoque l'exécution du thread dans le thread de l'interface utilisateur (le thread appelant, comme mentionné dans d'autres articles sur SO), ce qui n'est pas ce que je veux.

Un fil de discussion peut-il être démarré une seule fois? Si tel est le cas, que dois-je faire si je veux relancer le fil? Ce thread particulier fait un calcul en arrière-plan, si je ne le fais pas dans le thread, c'est fait dans le thread de l'interface utilisateur et que l'utilisateur a une attente déraisonnablement longue.

Volonté
la source
9
Pourquoi n'avez-vous pas simplement lu le javadoc - il décrit clairement le contrat.
mP.

Réponses:

111

À partir de la spécification de l'API Java pour la Thread.startméthode:

Il n'est jamais légal de démarrer un thread plus d'une fois. En particulier, un thread ne peut pas être redémarré une fois son exécution terminée.

En outre:

Jette:
IllegalThreadStateException- si le thread était déjà démarré.

Alors oui, a Threadne peut être démarré qu'une seule fois.

Si tel est le cas, que dois-je faire si je veux relancer le fil?

Si a Threaddoit être exécuté plus d'une fois, il faut créer une nouvelle instance de Threadet l'appeler start.

coobird
la source
Merci. J'ai vérifié la documentation avec l'IDE et le tutoriel Java pour les threads (et google aussi). Je vérifierai les spécifications de l'API à l'avenir. Cette critique "..ne jamais légal de commencer plus d'une fois .." n'est pas dans les autres lectures.
Sera
@coobird, si j'attribue l'ancien nom d'objet de thread à un nouveau Thread (), une fois l'ancien thread terminé, l'ancien thread sera-t-il ramassé (c'est-à-dire qu'il est automatiquement recyclé ou doit-il être fait explicitement)?
snapfractalpop
Il sera garbage collect, tant que le thread ne fonctionne plus.
avion le
1
Cette réponse est un peu dépassée. Si un programme Java moderne doit effectuer une tâche plus d'une fois, il ne doit pas en créer une nouvelle à Threadchaque fois. Au lieu de cela, il devrait soumettre la tâche à un pool de threads (par exemple java.util.concurrent.ThreadPoolExecutor)
Solomon Slow
13

Exactement raison. De la documentation :

Il n'est jamais légal de démarrer un thread plus d'une fois. En particulier, un thread ne peut pas être redémarré une fois son exécution terminée.

En ce qui concerne ce que vous pouvez faire pour des calculs répétés, il semble que vous puissiez utiliser la méthode SwingUtilities invokeLater . Vous expérimentez déjà l'appel run()direct, ce qui signifie que vous envisagez déjà d'utiliser un Runnableplutôt qu'un brut Thread. Essayez d'utiliser la invokeLaterméthode uniquement sur la Runnabletâche et voyez si cela correspond un peu mieux à votre schéma mental.

Voici l'exemple de la documentation:

 Runnable doHelloWorld = new Runnable() {
     public void run() {
         // Put your UI update computations in here.
         // BTW - remember to restrict Swing calls to the AWT Event thread.
         System.out.println("Hello World on " + Thread.currentThread());
     }
 };

 SwingUtilities.invokeLater(doHelloWorld);
 System.out.println("This might well be displayed before the other message.");

Si vous remplacez cet printlnappel par votre calcul, il se peut que ce soit exactement ce dont vous avez besoin.

EDIT: suite au commentaire, je n'avais pas remarqué la balise Android dans le message d'origine. L'équivalent d'invokeLater dans le travail Android est Handler.post(Runnable). Depuis son javadoc:

/**
 * Causes the Runnable r to be added to the message queue.
 * The runnable will be run on the thread to which this handler is
 * attached.
 *
 * @param r The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */

Ainsi, dans le monde Android, vous pouvez utiliser le même exemple que ci-dessus, en remplaçant le Swingutilities.invokeLaterpar le post approprié dans un fichier Handler.

Bob Cross
la source
L'OP pose des questions sur le threading sur Android, qui n'inclut pas les SwingUtilities.
Austyn Mahoney
@Austyn, vous avez raison. J'ai ajouté les remarques sur Handler.post () pour illustrer le code Android parallèle.
Bob Cross
1
Une autre façon si vous essayez simplement de mettre à jour votre interface utilisateur est d'utiliser RunOnUIThread(Runnable)ou View.post(Runnable)au lieu de créer votre propre gestionnaire. Ceux-ci exécuteront le runnable sur le thread principal vous permettant de mettre à jour l'interface utilisateur.
Austyn Mahoney
3

La réponse qui vient d'arriver explique pourquoi vous ne devriez pas faire ce que vous faites. Voici quelques options pour résoudre votre problème réel.

Ce thread particulier fait un calcul en arrière-plan, si je ne le fais pas dans le thread, c'est fait dans le thread de l'interface utilisateur et que l'utilisateur a une attente déraisonnablement longue.

Videz votre propre fil et utilisez-le AsyncTask.

Ou créez un nouveau fil lorsque vous en avez besoin.

Ou configurez votre thread pour qu'il fonctionne à partir d'une file d'attente de travail (par exemple LinkedBlockingQueue) plutôt que de redémarrer le thread.

CommonsWare
la source
3

Non , nous ne pouvons pas redémarrer Thread, cela lèvera runtimeException java.lang.IllegalThreadStateException. >

La raison est qu'une fois que la méthode run () est exécutée par Thread, elle passe à l'état mort.

Prenons un exemple - Penser à redémarrer le thread et à appeler la méthode start () dessus (qui en interne va appeler la méthode run ()) pour nous, c'est comme demander à un homme mort de se réveiller et de courir. Comme, après avoir terminé sa vie, la personne passe à l'état mort.

public class MyClass implements Runnable{

    @Override
    public void run() {
           System.out.println("in run() method, method completed.");
    }

    public static void main(String[] args) {
                  MyClass obj=new MyClass();            
        Thread thread1=new Thread(obj,"Thread-1");
        thread1.start();
        thread1.start(); //will throw java.lang.IllegalThreadStateException at runtime
    }

}

/ * OUTPUT dans la méthode run (), méthode terminée. Exception dans le thread "main" java.lang.IllegalThreadStateException à java.lang.Thread.start (source inconnue) * /

vérifie ça

Sameer Kazi
la source
2

Ce que vous devez faire est de créer un Runnable et de l'envelopper avec un nouveau Thread à chaque fois que vous souhaitez exécuter le Runnable. Ce serait vraiment moche à faire, mais vous pouvez Envelopper un thread avec un autre thread pour exécuter à nouveau le code, mais ne le faites que si vous le devez vraiment.

Peter Lawrey
la source
tout snipet, comment emballer?
Vinay
1

Comme vous l'avez dit, un thread ne peut pas être démarré plus d'une fois.

Directement de la bouche du cheval: Java API Spec

Il n'est jamais légal de démarrer un thread plus d'une fois. En particulier, un thread ne peut pas être redémarré une fois son exécution terminée.

Si vous avez besoin de réexécuter tout ce qui se passe dans votre thread, vous devrez créer un nouveau thread et l'exécuter.

alanlcode
la source
0

Réutiliser un thread est une action illégale dans l'API Java. Cependant, vous pouvez l'envelopper dans un outil exécutable et réexécuter cette instance à nouveau.

Aaron He
la source
0

Oui, nous ne pouvons pas démarrer déjà le thread. Il lèvera IllegalThreadStateException au moment de l'exécution - si le thread a déjà été démarré.

Que faire si vous avez vraiment besoin de démarrer le thread: Option 1) Si un thread doit être exécuté plus d'une fois, alors il faut créer une nouvelle instance du thread et appeler start dessus.

riteeka
la source
0

Un fil de discussion ne peut-il être démarré qu'une seule fois?

Oui. Vous pouvez le démarrer exactement une fois.

Si tel est le cas, que dois-je faire si je veux exécuter à nouveau le thread? Ce thread particulier fait un calcul en arrière-plan, si je ne le fais pas dans le thread, c'est fait dans le thread de l'interface utilisateur et que l'utilisateur a un une longue attente.

Ne Threadrecommencez pas. Créez plutôt Runnable et publiez-le sur Handler of HandlerThread . Vous pouvez soumettre plusieurs Runnableobjets. Si vous souhaitez renvoyer des données à UI Thread, avec votre Runnable run()méthode, publiez un Messagesur HandlerUI Thread et traitezhandleMessage

Reportez-vous à cet article pour un exemple de code:

Android: Toast dans un fil

Ravindra babu
la source
0

Je ne sais pas si c'est une bonne pratique, mais quand je laisse run () être appelé dans la méthode run (), cela ne génère aucune erreur et fait exactement ce que je voulais.

Je sais que cela ne recommence pas un fil, mais peut-être que cela vous sera utile.

public void run() {

    LifeCycleComponent lifeCycleComponent = new LifeCycleComponent();

    try {
        NetworkState firstState = lifeCycleComponent.getCurrentNetworkState();
        Thread.sleep(5000);
        if (firstState != lifeCycleComponent.getCurrentNetworkState()) {
            System.out.println("{There was a NetworkState change!}");
            run();
        } else {
            run();
        }
    } catch (SocketException | InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    Thread checkingNetworkStates = new Thread(new LifeCycleComponent());
    checkingNetworkStates.start();
}

J'espère que cela aide, même si c'est juste un peu.

À votre santé

Diorcula
la source
-1

Ce serait vraiment moche à faire, mais vous pouvez Envelopper un thread avec un autre thread pour exécuter à nouveau le code, mais ne le faites que si vous le devez vraiment.

J'ai dû réparer une fuite de ressources causée par un programmeur qui a créé un Thread mais au lieu de le démarrer (), il a appelé directement la méthode run (). Alors évitez-le, sauf si vous savez vraiment quels effets secondaires cela provoque.

Torben
la source
Mais la suggestion n'était pas d'appeler run()directement, mais simplement d'incorporer un Runnable dans un Thread et vraisemblablement l'invoquer start().
H2ONaCl
@ H2ONaCl Si vous lisez le texte que j'ai cité, la suggestion était d'envelopper un Thread dans un Thread. Il se peut que vous n'ayez pas lu la suggestion originale avant qu'elle ne soit modifiée.
Torben