2010-07-07 11 views

risposta

66

Il buon modo per farlo è quello di avere la run() del Filo custodito da una variabile boolean e impostarlo true dall'esterno quando si vuole fermare, qualcosa come:

class MyThread extends Thread 
{ 
    volatile boolean finished = false; 

    public void stopMe() 
    { 
    finished = true; 
    } 

    public void run() 
    { 
    while (!finished) 
    { 
     //do dirty work 
    } 
    } 
} 

C'era una volta un metodo stop() ma come afferma la documentazione

Questo metodo è intrinsecamente pericoloso. L'interruzione di una discussione con Thread.stop lo fa sbloccare tutti i monitor che ha bloccato (come conseguenza naturale dell'eccezione ThreadDeath non verificata che si propaga nello stack). Se uno qualsiasi degli oggetti precedentemente protetti da questi monitor si trovavano in uno stato incoerente, gli oggetti danneggiati diventano visibili ad altri thread, determinando potenzialmente un comportamento arbitrario.

Ecco perché si dovrebbe avere una guardia ..

+2

Vedi il commento di Jon Skeet alla risposta di Bart - vale anche per il tuo. –

+0

Se "finito" cambia mentre stai facendo il tuo "lavoro sporco" il thread non termina, allora devi fare un controllo periodico, ad esempio con un sonno (x) nel loop, qualcosa che non è raccomandato e ti dà una brutta prestazione, quindi è davvero un buon approccio? –

+0

stiamo parlando di chiudere un thread con grazia. Costringere un thread a smettere nel bel mezzo del suo sporco lavoro non è un modo elegante per farlo. Si dovrebbe sempre attendere l'iterazione successiva a meno che non si abbia un motivo specifico per interromperlo .. – Jack

4

Creare una variabile booleana stop da qualche parte. Poi nel codice che viene eseguito nel thread, regolarmente fare

if (stop) // end gracefully by breaking out of loop or whatever 

Per fermare il filo, impostare stop a true.

Penso che devi farlo manualmente in questo modo. Dopotutto, solo il codice in esecuzione nel thread ha qualche idea di cosa sia e non sia grazioso.

+21

Si noti che è necessario utilizzare il blocco o rendere volatile il campo per assicurarsi che il thread di lettura visualizzi le modifiche dal thread di scrittura. –

+0

@Jon Skeet: Sì, sarebbe meglio. Per curiosità, per quanto tempo l'arresto potrebbe essere ritardato se il campo non è volatile? –

+5

@Bart: in pratica, probabilmente non per molto tempo. In teoria, per sempre. –

10

Non si dovrebbe uccidere filo da altro. È considerato un'abitudine abbastanza brutta. Tuttavia, ci sono molti modi. È possibile utilizzare l'istruzione return dal metodo run del thread. Oppure puoi verificare se il thread è già stato interrotto e poi annullerà il suo lavoro. F.e. :

while (!isInterrupted()) { 
    // doStuff 
} 
+0

Il metodo run è nullo, non può restituire nulla. –

+3

è possibile utilizzare l'istruzione di reso vuota con tipo di reso vuoto, il metodo terminerà – Xorty

+0

questo è simile alla prima risposta, eccetto che si sta utilizzando il reso invece di lasciare semplicemente uscire il metodo run(). E sì, puoi semplicemente usare return; e che uscirà da un metodo di annullamento – Richard

3

È necessario inviare uno stop-messaggio al filo e il filo stesso deve agire se è stato ricevuto il messaggio. Questo è abbastanza facile, se l'azione di lunga durata è all'interno del ciclo:

public class StoppableThread extends Thread { 

    private volatile boolean stop = false; 

    public void stopGracefully() { 
    stop = true; 
    } 

    public void run() { 
    boolean finished = false; 
    while (!stop && !finished) { 
     // long running action - finished will be true once work is done 
    } 
    } 
} 
+1

Come Jon Skeet ha osservato sulla mia risposta, stop dovrebbe essere volatile –

+0

Ho modificato la mia risposta ma andrò a scoprire, perché si applica qui - nella tua risposta, ho pensato, doveva essere volatile solo perché non hai menzionato un metodo e sembrava che il campo 'stop' fosse accessibile direttamente. –

+1

Mi sono interrogato sulla parola chiave volatile prima e (senza tentare di pubblicizzare la mia domanda :)), ecco alcune risposte su di esso: http://stackoverflow.com/questions/106591/do-you-ever- use-the-volatile-keyword-in-java – Richard

12

La parte negativa sull'utilizzo di una bandiera per evitare che il filo è che se il thread è in attesa o dormire poi si deve aspettare che si Finisci di aspettare/dormire. Se si chiama il metodo di interrupt sul thread, questo causerà l'interruzione della chiamata di attesa o sospensione con un InterruptedException.

La chiamata interrupt() imposta anche una proprietà interrotta che è possibile utilizzare come flag per controllare se chiudere (nel caso in cui il thread non sia in attesa o inattivo).

È possibile scrivere il metodo di esecuzione del thread in modo che InterruptedException venga catturato al di fuori di qualsiasi logica di loop del thread, oppure è possibile rilevare l'eccezione all'interno del loop e chiudere la chiamata che genera l'eccezione, impostando il flag di interrupt all'interno del catch block per InterruptedException in modo che il thread non perda traccia del fatto che è stato interrotto. Il thread interrotto può ancora mantenere il controllo e terminare l'elaborazione alle sue condizioni.

Dire che voglio scrivere un thread di lavoro che funzioni in incrementi, dove c'è un sonno nel mezzo per qualche motivo, e non voglio uscire dal sonno per far sì che l'elaborazione si interrompa senza fare il lavoro rimanente per quell'incremento , voglio solo che per uscire se è in-tra incrementi:

class MyThread extends Thread 
{ 
    public void run() 
    { 
     while (!Thread.currentThread().isInterrupted()) 
     { 
      doFirstPartOfIncrement(); 
      try { 
       Thread.sleep(10000L); 
      } catch (InterruptedException e) { 
       // restore interrupt flag 
       Thread.currentThread().interrupt(); 
      } 
      doSecondPartOfIncrement(); 
     } 
    } 
} 

Here is an answer to a similar question, including example code.

1

per un filo di fermarsi, nessuno sembra aver menzionato (mis) con un'eccezione:

abstract class SelfStoppingThread extends Thread { 

    @Override 
    public final void run() { 
     try { 
      doRun(); 
     } catch (final Stop stop) { 
      //optional logging 
     } 
    } 

    abstract void doRun(); 

    protected final void stopSelf() { 
     throw new Stop(); 
    } 

    private static final class Stop extends RuntimeException {}; 

} 

Una sottoclasse deve solo eseguire override doRun() normalmente come si farebbe con una discussione e chiamare stopSelf() ogni volta che si desidera arrestarla. IMO sembra più pulito che usare un flag in un ciclo while.

Problemi correlati