2010-09-23 27 views
6

Sto scrivendo un'applicazione che genera più attività simultanee. Sto usando un pool di thread per implementarlo.come interrompere un thread in un threadpool

Può succedere che si verifichi un evento che rende non validi i calcoli eseguiti nelle attività. In tal caso, vorrei interrompere le attività attualmente in esecuzione e avviarne di nuove.

Il mio problema: come posso interrompere le attività attualmente in esecuzione? La soluzione che ho implementato è quella di memorizzare un riferimento al thread dell'attività e chiamare interrupt() su questo thread. Nel codice dimostrativo:

public class Task implements Runnable { 

    private String name; 
    private Thread runThread; 

    public Task(String name) { 
     super(); 
     this.name = name; 
    } 

    @Override 
    public void run() { 
     runThread = Thread.currentThread(); 

     System.out.println("Starting thread " + name); 
     while (true) { 
      try { 
       Thread.sleep(4000); 
       System.out.println("Hello from thread " + name); 
      } catch (InterruptedException e) { 
       // We've been interrupted: no more messages. 
       return; 
      } 
     } 
    } 

    public void stop() { 
     runThread.interrupt(); 
    } 

    public String getName() { 
     return name; 
    } 
} 

E il metodo principale è:

public static void main(String args[]) { 
    executorService = Executors.newFixedThreadPool(2); 

    Task t1 = new Task("Task1"); 
    Task t2 = new Task("Task2"); 
    executorService.execute(t1); 
    executorService.execute(t2); 
    executorService.execute(new Task("Task3")); 
    executorService.execute(new Task("Task4")); 

    try { 
     Thread.sleep(12000); 
     t1.stop(); 
     System.err.println("Stopped thread " + t1.getName()); 
     Thread.sleep(8000); 
     t2.stop(); 
     System.err.println("Stopped thread " + t2.getName()); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

Si tratta di una buona soluzione, o c'è un modo migliore per fermare un thread in esecuzione in un pool di thread?

+1

Estrarre la spina;) – sje397

risposta

2

Nel metodo run() sottoposto a override si termina per sempre con while(true). Il comportamento standard dovrebbe essere un boolean runIndicator che il metodo run() si imposta su true all'avvio e il ciclo dovrebbe essere while(runIndicator). Il metodo stop() deve essere impostato su runIndicator = false in modo che la prossima iterazione del ciclo si interrompa.

+0

In questa implementazione, anche il metodo 'stop()' dovrebbe chiamare 'this.interrupt()'. Nell'implementazione attuale, dovrebbe esserci un metodo simile che è possibile utilizzare per interrompere qualsiasi operazione di sollevamento pesante invece della chiamata 'Thread.sleep * (4000)'. È possibile utilizzare un metodo molto simile a 'runIndicator' per ottenere questo risultato. –

+0

@Erick, 'interrupt()' non è un metodo di eseguibile, quindi non è possibile. 'Thread.currentThread(). Interrupt()' non funzionerebbe neanche perché la chiamata di stop() si verifica in un altro thread rispetto a quello che si desidera interrompere. – Thirler

+1

Scusa, non l'ho capito. Estendo sempre 'Thread' invece di' Runnable', quindi ho sempre accesso a queste cose. Farei lo stesso qui. Questo risolve anche il problema secondario di sicurezza che hai postato nella tua risposta. –

3

L'idea alla base del vostro approccio è una delle varie soluzioni corrette. Dealing with InterruptedException offre un ottimo resoconto su come utilizzare il meccanismo di interrupt. Questo meccanismo è utile soprattutto quando si sono lunghi calcoli. Un'altra cosa da tenere a mente è che è possibile che altre librerie rovinino il meccanismo di interruzione non facendo ciò che dice la guida (non reimpostando lo stato di interruzione quando non lo hanno gestito, ecc.).

Si noti che la classe Task non è thread-safe. Potresti interrompere l'attività prima di salvare lo currentThread, che fornirebbe NullPointerException.

Un approccio molto più semplice è quello di impostare una variabile volatile booleanrunning e invece di un ciclo while(true) facendo un approccio while(running) (questo è comunque molto più generale).

Un'altra cosa da considerare è il meccanismo FutureTask, in quanto ha già un meccanismo di annullamento che utilizza il meccanismo di interrupt.

1

executorService.shutdown() ed executorService.shutdownNow() devono essere utilizzati per arrestare il pool di thread per uscire con garbo dall'applicazione. Vedi ExecutorService.

Vedere la risposta di Qwerky per terminare il thread attualmente in esecuzione.

+4

Vuole arrestare un singolo thread, non l'intero pool. –

+0

La risposta è data da Qwerky. Tuttavia, è necessario arrestare il pool di thread per uscire con garbo dall'applicazione. –

2

È possibile fermarlo tenendo un riferimento a quel futuro

  Future<?> future = exec.submit(new Runnable() { 
     while (true){ 
     try{ 
      obj.wait(); 
     }catch(InterruptedException e){ 
      System.out.println("interrupted"); 
      return; 
     } 
     }); 
     future.cancel(true); 

booleana è per - possono interrompere se in esecuzione.

Ho eseguito il test e ho ottenuto un'eccezione interrotta da quel thread.

Se si ha cacheThreadPool si consiglia di ricontrollare l'eccezione nel runnable e quindi non impostare il flag interrotto, poiché il thread eseguirà un altro futuro, se si imposta l'interrupt, l'altra coda il futuro potrebbe non funzionare.

Problemi correlati