È legale chiamare il metodo di avvio due volte sullo stesso thread?


90

Il codice seguente porta a java.lang.IllegalThreadStateException: Thread already startedquando ho chiamato il start()metodo la seconda volta nel programma.

updateUI.join();    

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

Questo accade la seconda volta che updateUI.start()viene chiamato. L'ho eseguito più volte e il thread viene chiamato e viene eseguito completamente fino al completamento prima di premere updateUI.start().

La chiamata updateUI.run()evita l'errore ma fa sì che il thread venga eseguito nel thread dell'interfaccia utente (il thread chiamante, come menzionato in altri post su SO), che non è quello che voglio.

Un thread può essere avviato solo una volta? In tal caso, cosa devo fare se voglio eseguire di nuovo il thread? Questo particolare thread sta eseguendo alcuni calcoli in background, se non lo faccio nel thread di quanto non sia stato fatto nel thread dell'interfaccia utente e l'utente ha un'attesa irragionevolmente lunga.


9
Perché non hai appena letto javadoc - descrive chiaramente il contratto.
mP.

Risposte:


112

Dalla specifica dell'API Java per il Thread.startmetodo:

Non è mai legale avviare un thread più di una volta. In particolare, un thread potrebbe non essere riavviato una volta completata l'esecuzione.

Inoltre:

Produce:
IllegalThreadStateException- se il thread è già stato avviato.

Quindi sì, a Threadpuò essere avviato solo una volta.

In tal caso, cosa devo fare se voglio eseguire di nuovo il thread?

Se a Threaddeve essere eseguito più di una volta, è necessario creare una nuova istanza di Threade richiamarlo start.


Grazie. Ho controllato la documentazione con l'IDE e il tutorial Java per i thread (e anche con Google). Controllerò le specifiche API in futuro. Quella critica "..non è mai legale iniziare più di una volta .." non è nelle altre letture.
Will

@coobird, se assegno il nome del vecchio oggetto thread a un nuovo Thread (), dopo che il vecchio thread è terminato, il vecchio thread verrà raccolto in modo indesiderato (ovvero viene riciclato automaticamente o deve essere fatto esplicitamente)?
snapfractalpop

Verrà raccolto dalla spazzatura, a condizione che il thread non sia più in esecuzione.
volo il

1
Questa risposta è un po 'datata. Se un programma Java moderno deve eseguire un'attività più di una volta, non dovrebbe crearne una nuova Threadogni volta. Invece, dovrebbe inviare l'attività a un pool di thread (ad esempio java.util.concurrent.ThreadPoolExecutor)
Solomon Slow

13

Completamente giusto. Dalla documentazione :

Non è mai legale avviare un thread più di una volta. In particolare, un thread potrebbe non essere riavviato una volta completata l'esecuzione.

In termini di ciò che puoi fare per calcoli ripetuti, sembra che potresti usare il metodo invokeLater di SwingUtilities . Stai già sperimentando la chiamata run()diretta, il che significa che stai già pensando di utilizzare un Runnablepiuttosto che un raw Thread. Prova a utilizzare il invokeLatermetodo solo per l' Runnableattività e vedi se si adatta un po 'meglio al tuo schema mentale.

Ecco l'esempio dalla documentazione:

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

Se sostituisci quella printlnchiamata con il tuo calcolo, potrebbe essere esattamente ciò di cui hai bisogno.

EDIT: in seguito al commento, non avevo notato il tag Android nel post originale. L'equivalente di invokeLater nel lavoro Android è Handler.post(Runnable). Dal suo 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.
 */

Quindi, nel mondo Android, puoi usare lo stesso esempio di cui sopra, sostituendo Swingutilities.invokeLatercon il post appropriato a un file Handler.


L'OP chiede informazioni sul threading su Android, che non include SwingUtilities.
Austyn Mahoney

@Austyn, hai ragione. Ho aggiunto le osservazioni su Handler.post () per illustrare il codice Android parallelo.
Bob Cross

1
Un altro modo se stai solo tentando di aggiornare la tua interfaccia utente è usare RunOnUIThread(Runnable)o View.post(Runnable)invece di creare il tuo gestore. Questi eseguiranno il runnable sul thread principale consentendo di aggiornare l'interfaccia utente.
Austyn Mahoney,

3

La risposta appena arrivata spiega perché non dovresti fare quello che stai facendo. Ecco alcune opzioni per risolvere il tuo problema reale.

Questo particolare thread sta eseguendo alcuni calcoli in background, se non lo faccio nel thread di quanto non sia stato fatto nel thread dell'interfaccia utente e l'utente ha un'attesa irragionevolmente lunga.

Scarica il tuo thread e usa AsyncTask.

Oppure crea un nuovo thread quando ne hai bisogno.

Oppure imposta il thread in modo che funzioni da una coda di lavoro (ad esempio LinkedBlockingQueue) invece di riavviare il thread.


3

No , non possiamo riavviare Thread, così facendo verrà generata runtimeException java.lang.IllegalThreadStateException. >

Il motivo è che una volta che il metodo run () viene eseguito da Thread, entra in uno stato morto.

Facciamo un esempio: pensare di riavviare il thread e chiamare il metodo start () su di esso (che internamente chiamerà il metodo run ()) per noi è un po 'come chiedere a un morto di svegliarsi e correre. Come, dopo aver completato la sua vita, la persona va allo stato morto.

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 nel metodo run (), metodo completato. Eccezione nel thread "main" java.lang.IllegalThreadStateException in java.lang.Thread.start (origine sconosciuta) * /

controllare questo


2

Quello che dovresti fare è creare un Runnable e avvolgerlo con un nuovo Thread ogni volta che vuoi eseguire il Runnable. Sarebbe davvero brutto da fare, ma puoi avvolgere un thread con un altro thread per eseguire nuovamente il codice, ma fallo solo se devi davvero.


qualsiasi snipet, come avvolgere?
Vinay

1

È come hai detto che un thread non può essere avviato più di una volta.

Direttamente dalla bocca del cavallo: Java API Spec

Non è mai legale avviare un thread più di una volta. In particolare, un thread potrebbe non essere riavviato una volta completata l'esecuzione.

Se hai bisogno di rieseguire qualunque cosa stia succedendo nel tuo thread, dovrai creare un nuovo thread ed eseguirlo.


0

Riutilizzare un thread è un'azione illegale nell'API Java. Tuttavia, è possibile racchiuderlo in un attrezzo eseguibile e rieseguire nuovamente l'istanza.


0

Sì, non possiamo iniziare già a eseguire il thread. Genererà IllegalThreadStateException in fase di esecuzione, se il thread è già stato avviato.

E se hai davvero bisogno di avviare il thread: Opzione 1) Se un thread deve essere eseguito più di una volta, è necessario creare una nuova istanza del thread e chiamare start su di esso.


0

Un thread può essere avviato solo una volta?

Sì. Puoi avviarlo esattamente una volta.

Se è così, cosa devo fare se voglio eseguire di nuovo il thread? Questo particolare thread sta eseguendo dei calcoli in background, se non lo faccio nel thread di quanto non sia stato fatto nel thread dell'interfaccia utente e l'utente ha un errore irragionevole Lunga attesa.

Non eseguire di Threadnuovo il file. Crea invece Runnable e pubblicalo su Handler of HandlerThread . Puoi inviare più Runnableoggetti. Se vuoi mandare indietro i dati di UI thread, con in vostro Runnable run()metodo, inviare un Messagesu Handlerdi UI thread e processihandleMessage

Fare riferimento a questo post per il codice di esempio:

Android: brindisi in un thread


0

Non so se sia una buona pratica, ma quando lascio che run () venga chiamato all'interno del metodo run () non genera errori e in realtà fa esattamente quello che volevo.

So che non sta ricominciando un thread, ma forse questo ti torna 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();
}

Spero che questo aiuti, anche se è solo un po '.

Saluti


-1

Sarebbe davvero brutto da fare, ma puoi avvolgere un thread con un altro thread per eseguire nuovamente il codice, ma fallo solo se devi davvero.

Ho dovuto riparare una perdita di risorse causata da un programmatore che ha creato un thread ma invece di avviarlo (), ha chiamato direttamente il metodo run (). Quindi evitatelo, a meno che non sappiate veramente quali effetti collaterali provoca.


Ma il suggerimento non era di chiamare run()direttamente, ma solo di incorporare un Runnable in un Thread e presumibilmente invocare start().
H2ONaCl

@ H2ONaCl Se hai letto il testo che ho citato, il suggerimento è stato di racchiudere un thread in un thread. È possibile che tu non abbia letto il suggerimento originale prima che fosse modificato.
Torben
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.