Come uccidi un thread in Java?


374

Come si uccide un java.lang.Threadin Java?


2
fino ad ora non puoi uccidere un thread; perché destroy () non viene mai implementato a causa del dead-lock incline
AZ_10

1
Preferisco la risposta per quanto ExecutorStatusriguarda questa domanda: stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Kirby

9
@loungerdork "Penso che Java dovrebbe implementare un metodo di arresto / distruzione sicuro per i thread in fuga su cui non hai alcun controllo, nonostante le avvertenze della perdita di blocchi e altre insidie" Quindi vuoi un arresto del thread non sicuro . Penso che tu ne abbia già uno.
DJClayworth,

4
È sorprendente che tipo di domande otterrebbero 212 voti nel 2009. Questo sarebbe immediatamente distrutto oggi.
Jonathon Reinhart,

7
@JonathonReinhart: Perché? Sembra essere una domanda legittima, anche in questi giorni. Forse non conosci la frustrazione che provoca quando hai discussioni in fuga e puoi usare solo funzioni deprecate per gestirlo in qualche modo?
TFuto

Risposte:


189

Vedi questa discussione di Sun sul perché si sono deprecatiThread.stop() . Descrive in dettaglio il motivo per cui si è trattato di un metodo errato e di cosa si dovrebbe fare per bloccare in sicurezza i thread in generale.

Il modo in cui raccomandano è di utilizzare una variabile condivisa come flag che richiede l'arresto del thread in background. Questa variabile può quindi essere impostata da un oggetto diverso che richiede la terminazione del thread.


1
se controlli il thread che hai interrotto isAlive () ti restituirà vero e continueranno ad aggiungersi al tuo ThreadGroup corrente [], puoi vederlo usando Thread.currentThread.getThreadGroup (). list (); stamperà tutti i thread che ha e vedrai più istanze del tuo thread se ripeti il ​​tuo flusso.
AZ_10

3
Se sei su un PC, allora non è un problema, ma se stai sviluppando un software per dispositivi mobili (Android l'ho sperimentato), otterrai OutOfMemoryError
AZ_

2
Si potrebbe / si dovrebbe notare che per garantire una comunicazione tempestiva della richiesta di arresto tramite flag, la variabile deve essere volatile (o l'accesso alla variabile deve essere sincronizzato), come indicato nella raccomandazione.
mtsz,

2
Quel collegamento è stato ucciso a questo punto. Sono stato in grado di trovarlo su archive.org, tuttavia: web.archive.org/web/20090202093154/http://java.sun.com/j2se/…
Jay Taylor,

2
Uso il metodo getConnection()da java.sql.DriverManager. Se il tentativo di connessione impiega troppo tempo, provo a terminare il thread corrispondente chiamando Thread.interrupt()ma non influenza affatto il thread. Il Thread.stop()funziona tuttavia, anche se l'oracolo dice che non dovrebbe funzionare, se interrupt()non lo fa. Mi chiedo come farlo funzionare ed evitare l'utilizzo del metodo obsoleto.
Danny Lo,

129

Generalmente non ..

Gli chiedi di interrompere qualunque cosa stia facendo usando Thread.interrupt () (collegamento javadoc)

Una buona spiegazione del perché è nel javadoc qui (collegamento java technote)


@Fredrik Cosa succede al contesto del thread quando interrupt()viene chiamato il metodo? La domanda principale è relativa alla generazione del registro per ogni nuovo thread.
ABcDexter,

3
@ABcDexter Il punto è che l'interrupt non interrompe nulla, segnala semplicemente al codice nel thread (o al codice che viene chiamato dal thread) che qualcuno gli ha chiesto di interrompere qualsiasi cosa stia facendo. Si suppone quindi che il thread arresti correttamente l'elaborazione e ritorni, proprio come se fosse fatto facendo ciò che dovrebbe (ea quel punto, probabilmente anche il contesto del thread viene scartato). OTOH, se avessi davvero forzato fermato il thread, la tua domanda sarebbe stata davvero buona e la risposta indefinita.
Fredrik,

64

In Java i thread non vengono uccisi, ma l'interruzione di un thread viene eseguita in modo cooperativo . Viene chiesto di terminare il thread e il thread può quindi arrestarsi con grazia.

Spesso volatile booleanviene utilizzato un campo che il thread controlla e termina periodicamente quando è impostato sul valore corrispondente.

Non userei a booleanper verificare se il thread dovrebbe terminare . Se si utilizza volatilecome modificatore di campo, funzionerà in modo affidabile, ma se il codice diventa più complesso, poiché utilizza invece altri metodi di blocco all'interno del whileciclo, potrebbe accadere che il codice non si interrompa affatto o almeno impiega più tempo potrebbe desiderare.

Alcuni metodi della libreria di blocco supportano l'interruzione.

Ogni thread ha già uno stato booleano interrotto e dovresti usarlo. Può essere implementato in questo modo:

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

public void cancel() { interrupt(); }

Codice sorgente adattato da Java Concurrency in Practice . Poiché il cancel()metodo è pubblico, puoi consentire a un altro thread di invocare questo metodo come desideri.


E cosa fare se si esegue codice non attendibile come plugin o script? Java ha sandbox incorporato per codice non attendibile. E quel sandbox è inutile, permette di lavorare senza fermi forzati. Immagina di scrivere un browser su Java. La capacità di uccidere script di pagine arbitrarie non ha prezzo.
Ayvango,

@ayvango Quindi devi eseguire quello script nella tua sandbox. Il sandbox Java protegge la macchina dall'applicazione, non parti dell'applicazione l'una dall'altra.
David Schwartz,

@DavidSchwartz, intendi dire che dovrei usare solo una piattaforma diversa da Java?
Ayvango

@ayvango Puoi comunque usare la sandbox Java per proteggere la macchina dalla tua applicazione. Ma se vuoi proteggere parti della tua applicazione da altre parti della tua applicazione, dovrai scegliere alcuni strumenti che possono farlo.
David Schwartz,

@DavidSchwartz ASFAIK non potrebbe esistere tali strumenti se non è supportato a livello di piattaforma. Ma ovviamente l'attività potrebbe essere risolta con un motore di script completamente interpretato. Potrebbe contare riduzioni come fa Erlang e fare altre cose.
Ayvango,

20

Un modo è quello di impostare una variabile di classe e usarla come sentinella.

Class Outer {
    public static volatile flag = true;

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

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

}

Impostare una variabile di classe esterna, ovvero flag = true nell'esempio sopra. Impostalo su false per "uccidere" il thread.


2
Proprio come un suggerimento laterale: una variabile come flag funziona solo quando il thread è in esecuzione e non è bloccato. Thread.interrupt () dovrebbe liberare il thread dalla maggior parte delle condizioni di attesa (attesa, sospensione, lettura di rete e così via). Pertanto non dovresti mai catturare InterruptedException per farlo funzionare.
ReneS,

12
Questo non è affidabile; fare "bandiera" volatileper assicurarsi che funzioni correttamente ovunque. La classe interna non è statica, quindi il flag dovrebbe essere una variabile di istanza. Il flag deve essere cancellato con un metodo di accesso in modo da poter eseguire altre operazioni (come l'interrupt). Il nome "flag" non è descrittivo.
Erickson,

2
Non capisco la cosa "while" nel metodo run. Questo non significa che qualunque cosa sia scritta nel metodo run verrà ripetuta? questo non è qualcosa che volevamo che il thread facesse in primo luogo :(

2
Si preferisce fare +1 mentre (! Thread.currentThread (). IsInteruppted ()) è preferito
Toby,

2
Entrambi i casi falliscono, quando ad es. Apri un processo esterno all'interno di while {// open ext process} e quel processo è bloccato, ora né il thread verrà interrotto né raggiungerà la fine per verificare la tua condizione booleana, e tu sei lasciato sospeso ... provalo ad es. avvia una console Python usando java.exec e prova a riavere il controllo senza scrivere exit, e vedi se c'è un modo per terminare quel processo ed uscire ... non c'è modo di uscire da tale situazione ...
Space Rocker

11

C'è un modo in cui puoi farlo. Ma se hai dovuto usarlo, o sei un cattivo programmatore o stai usando un codice scritto da programmatori cattivi. Quindi, dovresti pensare di smettere di essere un cattivo programmatore o di smettere di usare questo cattivo codice. Questa soluzione è solo per le situazioni in cui NON È NESSUN ALTRO MODO.

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
Non ci sono ragioni per farlo, perché è ancora possibile chiamare il pubblico Thread.stopanche se è deprecato.
Lii,

1
@Lii Thread.stopfa lo stesso ma controlla anche l'accesso e le autorizzazioni. L'uso Thread.stopè piuttosto ovvio e non ricordo il motivo per cui l'ho usato al Thread.stop0posto di quello. Forse Thread.stopnon ha funzionato per il mio caso speciale (Weblogic su Java 6). O forse perché Thread.stopè deprecato e causa un avvertimento.
VadimPlatonov,

1
Nel mio scenario questo era l'unico modo per fermare un thread in esecuzione senza fine. Per qualche motivo .stop () non ha fermato il thread, ma stop0 () ha fatto
fiffy

10

Voglio aggiungere diverse osservazioni, basate sui commenti accumulati.

  1. Thread.stop() interromperà un thread se il gestore della sicurezza lo consente.
  2. Thread.stop()è pericoloso. Detto questo, se stai lavorando in un ambiente JEE e non hai il controllo sul codice chiamato, potrebbe essere necessario; vedi Perché Thread.stop è deprecato?
  3. Non si dovrebbe mai smettere di fermare un thread di lavoro del contenitore. Se vuoi eseguire il codice che tende a bloccarsi, (attentamente) avvia un nuovo thread demone e monitoralo, uccidendo se necessario.
  4. stop()crea un nuovo ThreadDeathErrorerrore sul thread chiamante e quindi genera tale errore sul thread target . Pertanto, la traccia dello stack è generalmente priva di valore.
  5. In JRE 6, stop()verifica con il gestore della sicurezza e quindi chiama stop1()quelle chiamate stop0(). stop0()è un codice nativo.
  6. A partire da Java 13 Thread.stop()non è stato rimosso (ancora), ma è Thread.stop(Throwable)stato rimosso in Java 11. ( mailing list , JDK-8204243 )

8

Vorrei votare per Thread.stop().

Come ad esempio hai un'operazione di lunga durata (come una richiesta di rete). Presumibilmente stai aspettando una risposta, ma può richiedere del tempo e l'utente passa a un'altra interfaccia utente. Questo thread in attesa ora è a) inutile b) potenziale problema perché quando otterrà il risultato, è completamente inutile e innescherà richiamate che possono portare al numero di errori.

Tutto ciò e può eseguire un'elaborazione della risposta che potrebbe essere intensa per la CPU. E tu, come sviluppatore, non puoi nemmeno fermarlo, perché non puoi lanciare if (Thread.currentThread().isInterrupted())linee in tutto il codice.

Quindi l'incapacità di fermare forzatamente un thread è strano.


Se l'operazione di rete è già abilitata in modo sicuro, basta interrompere il thread. Se l'operazione di rete non è sicura, non puoi comunque chiamare in Thread.stop()modo sicuro. Non stai votando Thread.stop(), stai chiedendo ogni persona che implementa ogni operazione che potrebbe richiedere molto tempo per renderla sicura in modo sicuro. E questa potrebbe essere una buona idea, ma non ha nulla a che fare con l'implementazione Thread.stop()come modo per richiedere l'aborto sicuro. Ne abbiamo già interrupt.
David Schwartz,

"Quindi l'incapacità di fermare forzatamente un thread è strano." - ... finché non guardi in profondità (come hanno fatto i progettisti Java) e concludi che non ci sono soluzioni tecnicamente valide che non siano peggio del deprezzamento stop.
Stephen C,

5

La domanda è piuttosto vaga. Se intendevi "come faccio a scrivere un programma in modo che un thread smetta di funzionare quando lo voglio", allora varie altre risposte dovrebbero essere utili. Ma se volevi dire "Ho un'emergenza con un server che non posso riavviare in questo momento e ho solo bisogno di un thread particolare per morire, vieni cosa potrebbe", allora hai bisogno di uno strumento di intervento per abbinare strumenti di monitoraggio come jstack.

A tale scopo ho creato jkillthread . Vedi le sue istruzioni per l'uso.


Grazie! Esattamente quello che stavo cercando!
Denis Kokorin,

4

C'è ovviamente il caso in cui stai eseguendo una specie di codice non completamente affidabile. (Personalmente ho questo consentendo l'esecuzione degli script caricati nel mio ambiente Java. Sì, ci sono campanelli di allarme di sicurezza che suonano ovunque, ma fa parte dell'applicazione.) In questo sfortunato caso, prima di tutto stai semplicemente sperando chiedendo agli autori degli script per rispettare una sorta di segnale booleano run / non-run. L'unico decente fail safe è chiamare il metodo stop sul thread se, per esempio, dura più di un timeout.

Ma questo è semplicemente "decente", e non assoluto, perché il codice potrebbe catturare l'errore ThreadDeath (o qualunque altra eccezione lanciate esplicitamente), e non ripeterlo come dovrebbe fare un thread gentlemanly. Quindi, la linea di fondo è AFAIA, non esiste una sicurezza assoluta.


Sempre più servizi AFAIK stanno diventando ibridi e usano l'ambiente gestito per eseguire codice di terze parti (plugin, script, ecc.) Che non hanno il pieno controllo del codice, sembra irragionevole togliere completamente thread.stop dalla tabella, poiché per gli ingegneri di servizio, uno stato live-and-serving può essere infinitamente migliore di uno stato non servente (o a causa di un blocco (che elimina i thread) o di un loop infinito occupato (che elimina i core))
Weipeng L

3

Non c'è modo di uccidere con garbo un thread.

Puoi provare a interrompere il thread, una strategia comune è quella di utilizzare una pillola di veleno per inviare un messaggio al thread per fermarsi

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

Generalmente non si uccide, si interrompe o si interrompe un thread (o si verifica se viene interrotto ()), ma si lascia terminare naturalmente.

È semplice. È possibile utilizzare qualsiasi ciclo insieme al metodo (volatile) variabile booleana all'interno del metodo run () per controllare l'attività del thread. Puoi anche tornare dal thread attivo al thread principale per interromperlo.

In questo modo uccidi con garbo un thread :).


1

I tentativi di terminazione improvvisa del thread sono una cattiva pratica di programmazione ben nota e la prova di una cattiva progettazione dell'applicazione. Tutti i thread nell'applicazione multithread condividono esplicitamente e implicitamente lo stesso stato del processo e sono costretti a cooperare tra loro per mantenerlo coerente, altrimenti l'applicazione sarà soggetta a bug che saranno davvero difficili da diagnosticare. Pertanto, è responsabilità dello sviluppatore fornire una garanzia di tale coerenza attraverso un'attenta e chiara progettazione dell'applicazione.

Esistono due soluzioni principali per le terminazioni dei thread controllati:

  • Uso della bandiera volatile condivisa
  • Uso della coppia di metodi Thread.interrupt () e Thread.interrupted ().

Una spiegazione corretta e dettagliata dei problemi relativi alla terminazione dei thread improvvisi, nonché esempi di soluzioni errate e giuste per la terminazione dei thread controllati sono disponibili qui:

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


Poiché i programmi non sono più scritti da un singolo sviluppatore, spesso è necessario uccidere i thread. Pertanto non può essere considerata una cattiva programmazione. Non riesco a immaginare Linux senza uccidere. L'incapacità di uccidere i thread è un difetto di Java.
Pavel Niedoba,

1
Fantastico, signor Giusto ... E se dovessi chiamare una libreria di terze parti, su cui non hai controllo e che ha un timeout difettoso e che potrebbe bloccarsi una volta ogni 1000-2000 volte durante l'esecuzione? Cattiva pratica di programmazione eh? Bene, non puoi sempre avere accesso al codice che stai utilizzando. Ad ogni modo, l'opera chiede come uccidere un thread e non come controllarne il flusso quando si progetta il proprio codice ...
Arturas M

La domanda è: cosa succederà quando si uccide un thread che ha il mutex di proprietà o che ha allocato un blocco di memoria, o che dovrebbe generare qualche evento o dato che l'altro thread sta aspettando? Cosa succederà con il resto della logica della tua applicazione? Sarai sempre a rischio che il problema piccolo ed evidente venga convertito in problema complesso che sarà difficile da riprodurre e investigare. Uccidere il thread non è sicuro perché può lasciare la tua applicazione in diversi stati incoerenti. Dai un'occhiata alle informazioni dal link su cert.org.
Zarathustr,

Abbiamo un thread di controllo che a determinate condizioni può decidere che l'intero calcolo sia diventato semplicemente inutile. I molti runnable diversi che fanno il lavoro vero e proprio devono essere schizzati con una variante di isInterrupted () in qualsiasi posto conveniente - che orribile! È molto più bello se il controller fa sorgere un ThreadDeath apparentemente dal nulla, svolgendo in modo pulito tutti i blocchi sincronizzati e infine. Tutti dicono che questo è così soggetto a errori, ma per thread abbastanza non correlati, non vedo perché.
Daniel


1

Non ho interrotto il funzionamento in Android, quindi ho usato questo metodo, funziona perfettamente:

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
la parola chiave volatile deve essere aggiunta a shouldCheckUpdates, in caso di ottimizzazione del compilatore con l'archiviazione locale dei thread.
clinux,

1

"Uccidere un thread" non è la frase giusta da usare. Ecco un modo in cui possiamo implementare il grazioso completamento / uscita del thread su will:

Runnable che ho usato:

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 scatenante:

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

    }

}

la parola chiave volatile deve essere aggiunta alla variabile shouldStop, nel caso in cui il compilatore ottimizzi con l'archiviazione locale dei thread.
Oleksii Kyslytsyn,

0

Thread.stop è obsoleto, quindi come possiamo interrompere un thread in Java?

Utilizzare sempre il metodo di interruzione e futuro per richiedere la cancellazione

  1. Quando l'attività risponde al segnale di interruzione, ad esempio, il metodo di cattura della coda di blocco.
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. Quando l'attività non risponde al segnale di interruzione. Supponiamo che l'attività esegua I / O socket che non risponde al segnale di interruzione e quindi utilizzando l'approccio sopra non si interromperà l'attività, il timeout futuro sarebbe scaduto, ma il blocco di annullamento alla fine non avrà alcun effetto , il thread continuerà ad ascoltare socket. Possiamo chiudere il socket o chiamare il metodo close sulla connessione se implementato dal 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);
                }
            }

        };
    }
}
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.