Come interrompere un thread

Voglio eseguire un thread per un certo periodo di tempo. Se non è completato entro quel tempo, voglio ucciderlo, lanciare qualche eccezione, o gestirlo in qualche modo. Come può essere fatto?

Un modo per farlo come ho capito da questo thread è usare un TimerTask all’interno del metodo run () del Thread.

Ci sono soluzioni migliori per questo?

EDIT: aggiungere una taglia in quanto avevo bisogno di una risposta più chiara. Il codice ExecutorService fornito di seguito non risolve il mio problema. Perché dovrei dormire () dopo l’esecuzione (un po ‘di codice – non ho alcun controllo su questo pezzo di codice)? Se il codice è completato e il sleep () è interrotto, come può essere un timeout?

L’attività che deve essere eseguita non è nel mio controllo. Può essere qualsiasi pezzo di codice. Il problema è che questo pezzo di codice potrebbe essere eseguito in un ciclo infinito. Non voglio che ciò accada. Quindi, voglio solo eseguire quell’attività in un thread separato. Il thread padre deve attendere fino al termine del thread e deve conoscere lo stato dell’attività (ovvero se è scaduto il tempo o se si è verificata qualche eccezione o se è successo). Se l’attività si inserisce in un ciclo infinito, il mio thread principale continua ad attendere indefinitamente, il che non è una situazione ideale.

In effetti preferisco usare ExecutorService invece di Timer , ecco un SSCCE :

 package com.stackoverflow.q2275443; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class Test { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Task()); try { System.out.println("Started.."); System.out.println(future.get(3, TimeUnit.SECONDS)); System.out.println("Finished!"); } catch (TimeoutException e) { future.cancel(true); System.out.println("Terminated!"); } executor.shutdownNow(); } } class Task implements Callable { @Override public String call() throws Exception { Thread.sleep(4000); // Just to demo a long running task of 4 seconds. return "Ready!"; } } 

Gioca un po ‘con l’argomento di timeout nel metodo Future#get() , ad esempio aumentalo a 5 e vedrai che il thread finisce. È ansible intercettare il timeout nel catch (TimeoutException e) .

Aggiornamento: per chiarire un equivoco concettuale, il sleep() non è richiesto. È usato solo per scopi di dimostrazione / SSCCE. Esegui il tuo compito a lungo termine proprio lì al posto del sleep() . All’interno della tua attività a lungo termine, dovresti controllare se il thread non è stato interrotto come segue:

 while (!Thread.interrupted()) { // Do your long running task here. } 

Non esiste un modo affidabile al 100% per eseguire questa operazione per qualsiasi attività precedente. Il compito deve essere scritto tenendo presente questa capacità.

Le librerie Java core come ExecutorService annullano le attività asincrone con le chiamate interrupt() sul thread worker. Ad esempio, se l’attività contiene una sorta di loop, dovresti controllare il suo stato di interruzione su ogni iterazione. Se l’attività sta eseguendo operazioni di I / O, dovrebbero essere anche interrompibili e impostarle potrebbe essere complicato. In ogni caso, tieni presente che il codice deve controllare triggersmente gli interrupt; l’impostazione di un interrupt non fa necessariamente nulla.

Naturalmente, se il tuo compito è un semplice ciclo, puoi semplicemente controllare l’ora corrente ad ogni iterazione e rinunciare quando è scaduto un timeout specificato. In questo caso non è necessario un thread di lavoro.

Prendi in considerazione l’utilizzo di un’istanza di ExecutorService . Entrambi i invokeAll() e invokeAny() sono disponibili con un parametro di timeout .

Il thread corrente si bloccherà fino al completamento del metodo (non è sicuro se ciò sia desiderabile) perché le attività sono state completate normalmente o è stato raggiunto il timeout. È ansible ispezionare il (i) Future (i) restituito (i) per determinare cosa è successo.

BalusC ha detto:

Aggiornamento: per chiarire un equivoco concettuale, il sonno () non è richiesto. È usato solo per scopi di dimostrazione / SSCCE. Esegui il tuo compito a lungo termine proprio lì al posto del sonno ().

Ma se sostituisci Thread.sleep(4000); con for (int i = 0; i < 5E8; i++) {} allora non viene compilato, perché il ciclo vuoto non lancia un'interrupttionException.

E affinché il thread sia interrompibile, deve generare un InterruptedException .

Questo mi sembra un problema serio. Non riesco a vedere come adattare questa risposta per lavorare con un'attività generale di lunga durata.

Modificato per aggiungere: ho commentato questo come una nuova domanda: [ interrompendo un thread dopo il tempo fisso, deve lanciare InterruptedException? ]

Supponendo che il codice del thread sia fuori dal tuo controllo:

Dalla documentazione Java sopra menzionata:

Cosa succede se un thread non risponde a Thread.interrupt?

In alcuni casi, puoi usare trucchi specifici per l’applicazione. Ad esempio, se un thread è in attesa su un socket noto, è ansible chiudere il socket per fare in modo che il thread venga restituito immediatamente. Sfortunatamente, non esiste alcuna tecnica che funzioni in generale. Va notato che in tutte le situazioni in cui un thread in attesa non risponde a Thread.interrupt, non risponde nemmeno a Thread.stop. Tali casi includono attacchi deliberati di denial-of-service e operazioni di I / O per le quali thread.stop e thread.interrupt non funzionano correttamente.

Linea di fondo:

Assicurati che tutti i thread possano essere interrotti, oppure hai bisogno di una conoscenza specifica del thread – come avere un flag da impostare. Forse puoi richiedere che ti venga dato il compito insieme al codice necessario per fermarlo – definire un’interfaccia con un metodo stop() . È anche ansible avvisare quando non si è riusciti a interrompere un’attività.

Penso che dovresti dare un’occhiata ai meccanismi di gestione della concorrenza appropriati (i thread che girano in loop infiniti non sembrano buoni di per sé, btw). Assicurati di leggere un po ‘sull’argomento “uccisione” o “interruzione” dei thread .

Quello che stai descrivendo, sembra molto simile a un “rendez-vous”, quindi potresti voler dare un’occhiata al CyclicBarrier .

Potrebbero esserci altri costrutti (come ad esempio CountDownLatch per esempio) che possono risolvere il tuo problema (un thread in attesa con un timeout per il latch, l’altro dovrebbe eseguire il conto alla rovescia del latch se ha funzionato, che rilascia il tuo primo thread dopo un timeout o quando viene richiamato il conto alla rovescia del latch).

Di solito raccomando due libri in quest’area: Concurrent Programming in Java e Java Concurrency in Practice .

Ho creato una class di supporto solo per questo qualche tempo fa. Funziona alla grande:

 import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * TimeOut class - used for stopping a thread that is taking too long * @author Peter Goransson * */ public class TimeOut { Thread interrupter; Thread target; long timeout; boolean success; boolean forceStop; CyclicBarrier barrier; /** * * @param target The Runnable target to be executed * @param timeout The time in milliseconds before target will be interrupted or stopped * @param forceStop If true, will Thread.stop() this target instead of just interrupt() */ public TimeOut(Runnable target, long timeout, boolean forceStop) { this.timeout = timeout; this.forceStop = forceStop; this.target = new Thread(target); this.interrupter = new Thread(new Interrupter()); barrier = new CyclicBarrier(2); // There will always be just 2 threads waiting on this barrier } public boolean execute() throws InterruptedException { // Start target and interrupter target.start(); interrupter.start(); // Wait for target to finish or be interrupted by interrupter target.join(); interrupter.interrupt(); // stop the interrupter try { barrier.await(); // Need to wait on this barrier to make sure status is set } catch (BrokenBarrierException e) { // Something horrible happened, assume we failed success = false; } return success; // status is set in the Interrupter inner class } private class Interrupter implements Runnable { Interrupter() {} public void run() { try { Thread.sleep(timeout); // Wait for timeout period and then kill this target if (forceStop) { target.stop(); // Need to use stop instead of interrupt since we're trying to kill this thread } else { target.interrupt(); // Gracefully interrupt the waiting thread } System.out.println("done"); success = false; } catch (InterruptedException e) { success = true; } try { barrier.await(); // Need to wait on this barrier } catch (InterruptedException e) { // If the Child and Interrupter finish at the exact same millisecond we'll get here // In this weird case assume it failed success = false; } catch (BrokenBarrierException e) { // Something horrible happened, assume we failed success = false; } } } } 

Si chiama così:

 long timeout = 10000; // number of milliseconds before timeout TimeOut t = new TimeOut(new PhotoProcessor(filePath, params), timeout, true); try { boolean sucess = t.execute(); // Will return false if this times out if (!sucess) { // This thread timed out } else { // This thread ran completely and did not timeout } } catch (InterruptedException e) {} 

Ti mando un pezzo di codice che mostra come risolvere il problema. Come esempio sto leggendo un file. È ansible utilizzare questo metodo per un’altra operazione, ma è necessario implementare il metodo kill () in modo che l’operazione principale venga interrotta.

spero che sia d’aiuto

 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * Main class * * @author el * */ public class Main { /** * Thread which perform the task which should be timed out. * * @author el * */ public static class MainThread extends Thread { /** * For example reading a file. File to read. */ final private File fileToRead; /** * InputStream from the file. */ final private InputStream myInputStream; /** * Thread for timeout. */ final private TimeOutThread timeOutThread; /** * true if the thread has not ended. */ boolean isRunning = true; /** * true if all tasks where done. */ boolean everythingDone = false; /** * if every thing could not be done, an {@link Exception} may have * Happens. */ Throwable endedWithException = null; /** * Constructor. * * @param file * @throws FileNotFoundException */ MainThread(File file) throws FileNotFoundException { setDaemon(false); fileToRead = file; // open the file stream. myInputStream = new FileInputStream(fileToRead); // Instantiate the timeout thread. timeOutThread = new TimeOutThread(10000, this); } /** * Used by the {@link TimeOutThread}. */ public void kill() { if (isRunning) { isRunning = false; if (myInputStream != null) { try { // close the stream, it may be the problem. myInputStream.close(); } catch (IOException e) { // Not interesting System.out.println(e.toString()); } } synchronized (this) { notify(); } } } /** * The task which should be timed out. */ @Override public void run() { timeOutThread.start(); int bytes = 0; try { // do something while (myInputStream.read() >= 0) { // may block the thread. myInputStream.read(); bytes++; // simulate a slow stream. synchronized (this) { wait(10); } } everythingDone = true; } catch (IOException e) { endedWithException = e; } catch (InterruptedException e) { endedWithException = e; } finally { timeOutThread.kill(); System.out.println("-->read " + bytes + " bytes."); isRunning = false; synchronized (this) { notifyAll(); } } } } /** * Timeout Thread. Kill the main task if necessary. * * @author el * */ public static class TimeOutThread extends Thread { final long timeout; final MainThread controlledObj; TimeOutThread(long timeout, MainThread controlledObj) { setDaemon(true); this.timeout = timeout; this.controlledObj = controlledObj; } boolean isRunning = true; /** * If we done need the {@link TimeOutThread} thread, we may kill it. */ public void kill() { isRunning = false; synchronized (this) { notify(); } } /** * */ @Override public void run() { long deltaT = 0l; try { long start = System.currentTimeMillis(); while (isRunning && deltaT < timeout) { synchronized (this) { wait(Math.max(100, timeout - deltaT)); } deltaT = System.currentTimeMillis() - start; } } catch (InterruptedException e) { // If the thread is interrupted, // you may not want to kill the main thread, // but probably yes. } finally { isRunning = false; } controlledObj.kill(); } } /** * Start the main task and wait for the end. * * @param args * @throws FileNotFoundException */ public static void main(String[] args) throws FileNotFoundException { long start = System.currentTimeMillis(); MainThread main = new MainThread(new File(args[0])); main.start(); try { while (main.isRunning) { synchronized (main) { main.wait(1000); } } long stop = System.currentTimeMillis(); if (main.everythingDone) System.out.println("all done in " + (stop - start) + " ms."); else { System.out.println("could not do everything in " + (stop - start) + " ms."); if (main.endedWithException != null) main.endedWithException.printStackTrace(); } } catch (InterruptedException e) { System.out.println("You've killed me!"); } } } 

Saluti

Il seguente frammento inizierà un’operazione in un thread separato, quindi attenderà fino a 10 secondi per il completamento dell’operazione. Se l’operazione non viene completata in tempo, il codice tenterà di annullare l’operazione, quindi proseguirà in modo allegro. Anche se l’operazione non può essere annullata facilmente, il thread padre non aspetterà il termine del thread secondario.

 ExecutorService executorService = getExecutorService(); Future future = executorService.submit(new Callable() { public SomeClass call() { // Perform long-running task, return result. The code should check // interrupt status regularly, to facilitate cancellation. } }); try { // Real life code should define the timeout as a constant or // retrieve it from configuration SomeClass result = future.get(10, TimeUnit.SECONDS); // Do something with the result } catch (TimeoutException e) { future.cancel(true); // Perform other error handling, eg logging, throwing an exception } 

Il metodo getExecutorService() può essere implementato in diversi modi. Se non si hanno requisiti particolari, è sufficiente chiamare Executors.newCachedThreadPool() per il pool di thread senza limite superiore sul numero di thread.

Una cosa che non ho visto menzionato è che uccidere discussioni è generalmente una ctriggers idea. Esistono tecniche per rendere i metodi di thread facilmente modificabili , ma è diverso dall’uccidere un thread dopo un timeout.

Il rischio con quello che stai suggerendo è che probabilmente non sai in che stato sarà il thread quando lo uccidi, quindi rischi di introdurre instabilità. Una soluzione migliore consiste nell’assicurarsi che il codice filettato non si blocchi da solo o che risponda correttamente a una richiesta di interruzione.

Ecco la mia class helper davvero semplice da usare per eseguire o chiamare un pezzo di codice Java 🙂

Questo si basa sull’eccellente risposta di BalusC

 package com.mycompany.util.concurrent; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Calling {@link Callable#call()} or Running {@link Runnable#run()} code * with a timeout based on {@link Future#get(long, TimeUnit))} * @author pascaldalfarra * */ public class CallableHelper { private CallableHelper() { } public static final void run(final Runnable runnable, int timeoutInSeconds) { run(runnable, null, timeoutInSeconds); } public static final void run(final Runnable runnable, Runnable timeoutCallback, int timeoutInSeconds) { call(new Callable() { @Override public Void call() throws Exception { runnable.run(); return null; } }, timeoutCallback, timeoutInSeconds); } public static final  T call(final Callable callable, int timeoutInSeconds) { return call(callable, null, timeoutInSeconds); } public static final  T call(final Callable callable, Runnable timeoutCallback, int timeoutInSeconds) { ExecutorService executor = Executors.newSingleThreadExecutor(); try { Future future = executor.submit(callable); T result = future.get(timeoutInSeconds, TimeUnit.SECONDS); System.out.println("CallableHelper - Finished!"); return result; } catch (TimeoutException e) { System.out.println("CallableHelper - TimeoutException!"); if(timeoutCallback != null) { timeoutCallback.run(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } finally { executor.shutdownNow(); executor = null; } return null; } } 

Ottima risposta di BalusC:

ma giusto per aggiungere che il timeout stesso non interrompe il thread stesso. anche se stai controllando mentre (! Thread.interrupted ()) nel tuo compito. se vuoi assicurarti che il thread sia fermato devi anche assicurarti che future.cancel () venga richiamato quando l’eccezione di timeout è catch.

 package com.stackoverflow.q2275443; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class Test { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Task()); try { System.out.println("Started.."); System.out.println(future.get(3, TimeUnit.SECONDS)); System.out.println("Finished!"); } catch (TimeoutException e) { //Without the below cancel the thread will continue to live // even though the timeout exception thrown. future.cancel(); System.out.println("Terminated!"); } executor.shutdownNow(); } } class Task implements Callable { @Override public String call() throws Exception { while(!Thread.currentThread.isInterrupted()){ System.out.println("Im still running baby!!"); } } } 

Penso che la risposta dipenda principalmente dal compito stesso.

  • Sta facendo un compito più e più volte?
  • È necessario che il timeout interrompa un’attività in esecuzione immediatamente dopo la sua scadenza?

Se la prima risposta è sì e la seconda è no, puoi mantenerla così semplice:

 public class Main { private static final class TimeoutTask extends Thread { private final long _timeoutMs; private Runnable _runnable; private TimeoutTask(long timeoutMs, Runnable runnable) { _timeoutMs = timeoutMs; _runnable = runnable; } @Override public void run() { long start = System.currentTimeMillis(); while (System.currentTimeMillis() < (start + _timeoutMs)) { _runnable.run(); } System.out.println("execution took " + (System.currentTimeMillis() - start) +" ms"); } } public static void main(String[] args) throws Exception { new TimeoutTask(2000L, new Runnable() { @Override public void run() { System.out.println("doing something ..."); try { // pretend it's taking somewhat longer than it really does Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } }).start(); } } 

Se questa non è un'opzione, si prega di restringere i requisiti - o mostrare un codice.

Stavo cercando un ExecutorService che possa interrompere tutti i Runnable scaduti, ma non trovato. Dopo alcune ore ne ho creato uno come di seguito. Questa class può essere modificata per migliorare la robustezza.

 public class TimedExecutorService extends ThreadPoolExecutor { long timeout; public TimedExecutorService(int numThreads, long timeout, TimeUnit unit) { super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(numThreads + 1)); this.timeout = unit.toMillis(timeout); } @Override protected void beforeExecute(Thread thread, Runnable runnable) { Thread interruptionThread = new Thread(new Runnable() { @Override public void run() { try { // Wait until timeout and interrupt this thread Thread.sleep(timeout); System.out.println("The runnable times out."); thread.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } }); interruptionThread.start(); } } 

Uso:

 public static void main(String[] args) { Runnable abcdRunnable = new Runnable() { @Override public void run() { System.out.println("abcdRunnable started"); try { Thread.sleep(20000); } catch (InterruptedException e) { // logger.info("The runnable times out."); } System.out.println("abcdRunnable ended"); } }; Runnable xyzwRunnable = new Runnable() { @Override public void run() { System.out.println("xyzwRunnable started"); try { Thread.sleep(20000); } catch (InterruptedException e) { // logger.info("The runnable times out."); } System.out.println("xyzwRunnable ended"); } }; int numThreads = 2, timeout = 5; ExecutorService timedExecutor = new TimedExecutorService(numThreads, timeout, TimeUnit.SECONDS); timedExecutor.execute(abcdRunnable); timedExecutor.execute(xyzwRunnable); timedExecutor.shutdown(); } 

Ora, ho un problema come questo. Capita di decodificare l’immagine. Il processo di decodifica impiega troppo tempo a mantenere lo schermo nero. Aggiungo un controllore orario: quando il tempo è troppo lungo, fai un salto dal thread corrente. Quanto segue è il diff:

  ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Callable() { @Override public Bitmap call() throws Exception { Bitmap bitmap = decodeAndScaleBitmapFromStream(context, inputUri);// do some time consuming operation return null; } }); try { Bitmap result = future.get(1, TimeUnit.SECONDS); } catch (TimeoutException e){ future.cancel(true); } executor.shutdown(); return (bitmap!= null); 

Ho avuto lo stesso problema. Quindi ho trovato una soluzione semplice come questa.

 public class TimeoutBlock { private final long timeoutMilliSeconds; private long timeoutInteval=100; public TimeoutBlock(long timeoutMilliSeconds){ this.timeoutMilliSeconds=timeoutMilliSeconds; } public void addBlock(Runnable runnable) throws Throwable{ long collectIntervals=0; Thread timeoutWorker=new Thread(runnable); timeoutWorker.start(); do{ if(collectIntervals>=this.timeoutMilliSeconds){ timeoutWorker.stop(); throw new Exception("< <<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated."); } collectIntervals+=timeoutInteval; Thread.sleep(timeoutInteval); }while(timeoutWorker.isAlive()); System.out.println("< <<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds."); } /** * @return the timeoutInteval */ public long getTimeoutInteval() { return timeoutInteval; } /** * @param timeoutInteval the timeoutInteval to set */ public void setTimeoutInteval(long timeoutInteval) { this.timeoutInteval = timeoutInteval; } } 

Garantisce che se il blocco non è stato eseguito entro il limite di tempo. il processo terminerà e genererà un’eccezione.

esempio :

 try { TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds Runnable block=new Runnable() { @Override public void run() { //TO DO write block of code } }; timeoutBlock.addBlock(block);// execute the runnable block } catch (Throwable e) { //catch the exception here . Which is block didn't execute within the time limit }