Ist es legal, die Startmethode zweimal im selben Thread aufzurufen?

89

Der folgende Code führt dazu, java.lang.IllegalThreadStateException: Thread already starteddass ich die start()Methode zum zweiten Mal im Programm aufgerufen habe .

updateUI.join();    

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

Dies geschieht , das zweite Mal updateUI.start()aufgerufen wird. Ich habe es mehrmals durchlaufen und der Thread wird aufgerufen und läuft vollständig bis zum Abschluss, bevor er getroffen wird updateUI.start().

Das Aufrufen updateUI.run()vermeidet den Fehler, führt jedoch dazu, dass der Thread im UI-Thread ausgeführt wird (der aufrufende Thread, wie in anderen Beiträgen zu SO erwähnt), was ich nicht möchte.

Kann ein Thread nur einmal gestartet werden? Wenn ja, was mache ich, wenn ich den Thread erneut ausführen möchte? Dieser bestimmte Thread führt einige Berechnungen im Hintergrund durch, wenn ich dies nicht im Thread als im UI-Thread mache und der Benutzer eine unangemessen lange Wartezeit hat.

Wille
quelle
9
Warum hast du nicht gerade das Javadoc gelesen - es beschreibt den Vertrag klar.
mP.

Antworten:

110

Aus der Java-API-Spezifikation für die Thread.startMethode:

Es ist niemals legal, einen Thread mehr als einmal zu starten. Insbesondere darf ein Thread nach Abschluss der Ausführung nicht mehr neu gestartet werden.

Außerdem:

Auslöser:
IllegalThreadStateException- Wenn der Thread bereits gestartet wurde.

Also ja, a Threadkann nur einmal gestartet werden.

Wenn ja, was mache ich, wenn ich den Thread erneut ausführen möchte?

Wenn a Threadmehr als einmal ausgeführt werden muss, sollte eine neue Instanz von erstellt Threadund aufgerufen werden start.

Coobird
quelle
Vielen Dank. Ich habe die Dokumentation mit der IDE und dem Java-Tutorial auf Threads überprüft (und auch auf Google). Ich werde die API-Spezifikation in Zukunft überprüfen. Das kritische "..nie legal, mehr als einmal zu beginnen .." ist nicht in den anderen Lesungen.
Will
@coobird, wenn ich den alten Thread-Objektnamen einem neuen Thread () zuordne, wird der alte Thread nach Abschluss des alten Threads müllsammelt (dh wird er automatisch recycelt oder muss dies explizit erfolgen)?
snapfractalpop
Es wird Müll gesammelt, solange der Thread nicht mehr läuft.
fliegen
1
Diese Antwort ist etwas veraltet. Wenn ein modernes Java-Programm eine Aufgabe mehr als einmal ausführen muss, sollte es nicht Threadjedes Mal eine neue erstellen . Stattdessen sollte es die Aufgabe an einen Thread-Pool (z. B. java.util.concurrent.ThreadPoolExecutor) senden
Solomon Slow
13

Genau richtig. Aus der Dokumentation :

Es ist niemals legal, einen Thread mehr als einmal zu starten. Insbesondere darf ein Thread nach Abschluss der Ausführung nicht mehr neu gestartet werden.

In Bezug auf das, was Sie für wiederholte Berechnungen tun können, scheint es, als könnten Sie die SwokeUtilities-Methode invokeLater verwenden . Sie experimentieren bereits mit dem run()direkten Anrufen , was bedeutet, dass Sie bereits darüber nachdenken, Runnableein Raw anstelle eines Raw zu verwenden Thread. Versuchen Sie, die invokeLaterMethode nur für die RunnableAufgabe zu verwenden, und prüfen Sie, ob dies etwas besser zu Ihrem mentalen Muster passt.

Hier ist das Beispiel aus der Dokumentation:

 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.");

Wenn Sie diesen printlnAufruf durch Ihre Berechnung ersetzen, ist er möglicherweise genau das, was Sie benötigen.

BEARBEITEN: Nach dem Kommentar hatte ich das Android-Tag im ursprünglichen Beitrag nicht bemerkt. Das Äquivalent zu invokeLater in der Android-Arbeit ist Handler.post(Runnable). Aus seinem 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.
 */

In der Android-Welt können Sie also dasselbe Beispiel wie oben verwenden und das Swingutilities.invokeLaterdurch den entsprechenden Beitrag in a ersetzen Handler.

Bob Cross
quelle
Das OP fragt nach Threading unter Android, das die SwingUtilities nicht enthält.
Austyn Mahoney
@Austyn, du hast recht. Ich habe die Anmerkungen zu Handler.post () hinzugefügt, um den parallelen Android-Code zu veranschaulichen.
Bob Cross
1
Eine andere Möglichkeit, wenn Sie nur versuchen, Ihre Benutzeroberfläche zu aktualisieren, besteht darin , einen eigenen Handler zu verwenden RunOnUIThread(Runnable)oder View.post(Runnable)zu erstellen. Diese führen die ausführbare Datei im Hauptthread aus, sodass Sie die Benutzeroberfläche aktualisieren können.
Austyn Mahoney
3

Die gerade eingetroffene Antwort behandelt, warum Sie nicht tun sollten, was Sie tun. Hier sind einige Optionen zur Lösung Ihres eigentlichen Problems.

Dieser bestimmte Thread führt einige Berechnungen im Hintergrund durch, wenn ich dies nicht im Thread als im UI-Thread mache und der Benutzer eine unangemessen lange Wartezeit hat.

Dump deinen eigenen Thread und benutze AsyncTask.

Oder erstellen Sie einen neuen Thread, wenn Sie ihn benötigen.

Oder richten Sie Ihren Thread so ein, dass er außerhalb einer Arbeitswarteschlange (z. B. LinkedBlockingQueue) ausgeführt wird, anstatt den Thread neu zu starten.

CommonsWare
quelle
3

Nein , wir können Thread nicht erneut starten. Dadurch wird die runtimeException java.lang.IllegalThreadStateException ausgelöst. >

Der Grund dafür ist, dass die run () -Methode, sobald sie von Thread ausgeführt wurde, in den toten Zustand übergeht.

Nehmen wir ein Beispiel: Wenn wir daran denken, den Thread erneut zu starten und die start () -Methode darauf aufzurufen (die intern die run () -Methode aufruft), ist dies so etwas wie die Aufforderung an den Toten, aufzuwachen und auszuführen. Als nach Beendigung seines Lebens geht die Person in den toten Zustand.

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 in der Methode run (), Methode abgeschlossen. Ausnahme im Thread "main" java.lang.IllegalThreadStateException bei java.lang.Thread.start (unbekannte Quelle) * /

Überprüfen Sie dies

Sameer Kazi
quelle
2

Sie sollten eine ausführbare Datei erstellen und sie jedes Mal, wenn Sie die ausführbare Datei ausführen möchten, mit einem neuen Thread versehen. Es wäre wirklich hässlich, dies zu tun, aber Sie können einen Thread mit einem anderen Thread umbrechen, um den Code erneut auszuführen, aber nur dies müssen Sie wirklich tun.

Peter Lawrey
quelle
irgendein Snipet, wie man es einwickelt?
Vinay
1

Wie Sie sagten, kann ein Thread nicht mehr als einmal gestartet werden.

Direkt aus dem Maul des Pferdes: Java API Spec

Es ist niemals legal, einen Thread mehr als einmal zu starten. Insbesondere darf ein Thread nach Abschluss der Ausführung nicht mehr neu gestartet werden.

Wenn Sie erneut ausführen müssen, was in Ihrem Thread vor sich geht, müssen Sie einen neuen Thread erstellen und diesen ausführen.

Alanlcode
quelle
0

Die Wiederverwendung eines Threads ist eine unzulässige Aktion in der Java-API. Sie können es jedoch in eine ausführbare Implementierung einbinden und diese Instanz erneut ausführen.

Aaron He
quelle
0

Ja, wir können den Thread nicht bereits ausführen. Es wird zur Laufzeit eine IllegalThreadStateException auslösen - wenn der Thread bereits gestartet wurde.

Was ist, wenn Sie den Thread wirklich starten müssen: Option 1) Wenn ein Thread mehr als einmal ausgeführt werden muss, sollte eine neue Instanz des Threads erstellt und start darauf aufgerufen werden.

Riteeka
quelle
0

Kann ein Thread nur einmal gestartet werden?

Ja. Sie können es genau einmal starten.

Wenn ja, was mache ich, wenn ich den Thread erneut ausführen möchte? Dieser bestimmte Thread führt im Hintergrund einige Berechnungen durch, wenn ich dies nicht im Thread mache, als im UI-Thread, und der Benutzer hat einen unangemessenen langes Warten.

Lass das nicht noch Threadeinmal laufen . Erstellen Sie stattdessen Runnable und veröffentlichen Sie es auf Handler of HandlerThread . Sie können mehrere RunnableObjekte einreichen . Wenn Sie wollen , Daten zurück zu UI - Thread zu schicken, mit in Ihre Runnable run()Methode, Posten Messageauf Handlerder UI - Thread und ProzesshandleMessage

In diesem Beitrag finden Sie Beispielcode:

Android: Toast in einem Thread

Ravindra Babu
quelle
0

Ich weiß nicht, ob es eine gute Praxis ist, aber wenn ich run () innerhalb der run () -Methode aufrufen lasse, wird kein Fehler ausgegeben und es wird genau das getan, was ich wollte.

Ich weiß, dass es keinen Thread erneut startet, aber vielleicht ist dies für Sie nützlich.

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();
}

Hoffe das hilft, auch wenn es nur ein bisschen ist.

Prost

Diorcula
quelle
-1

Es wäre wirklich hässlich, dies zu tun, aber Sie können einen Thread mit einem anderen Thread umbrechen, um den Code erneut auszuführen, aber nur dies müssen Sie wirklich tun.

Ich musste ein Ressourcenleck beheben, das von einem Programmierer verursacht wurde, der einen Thread erstellt hat, aber anstatt ihn zu starten (), rief er die run () - Methode direkt auf. Vermeiden Sie es also, es sei denn, Sie wissen wirklich, welche Nebenwirkungen es verursacht.

Torben
quelle
Der Vorschlag war jedoch, nicht run()direkt aufzurufen , sondern nur ein Runnable in einen Thread einzubetten und vermutlich aufzurufen start().
H2ONaCl
@ H2ONaCl Wenn Sie den von mir zitierten Text lesen, war der Vorschlag, einen Thread in einen Thread zu wickeln. Es kann sein, dass Sie den ursprünglichen Vorschlag nicht gelesen haben, bevor er bearbeitet wurde.
Torben