Come eseguire il timeout di una discussione


255

Voglio eseguire un thread per un determinato periodo di tempo. Se non viene completato entro quel tempo, voglio ucciderlo, lanciare qualche eccezione o gestirlo in qualche modo. Come si può fare?

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: aggiungendo una taglia come avevo bisogno di una risposta più chiara. Il codice ExecutorService indicato 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 sleep () viene interrotto, come può essere timeOut?

L'attività che deve essere eseguita non è sotto il mio controllo. Può essere qualsiasi 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 principale deve attendere fino al termine del thread e deve conoscere lo stato dell'attività (ovvero se è scaduto o si è verificata qualche eccezione o se ha esito positivo). Se l'attività va in un ciclo infinito, il mio thread genitore continua ad aspettare indefinitamente, il che non è una situazione ideale.


EDIT: Aggiunta di una taglia poiché avevo bisogno di una risposta più chiara. il codice ExecutorService indicato di seguito non risolve il mio problema. Perché dovrei dormire () dopo aver eseguito il mio codice? Se il codice è completato e sleep () viene interrotto, come può essere timeOut?
java_geek,

7
Quello sleep()era solo uno stub per rappresentare "attività da molto tempo in esecuzione". Basta sostituirlo con il tuo vero compito;)
BalusC

1
... un "compito da molto tempo in esecuzione" che capita di rispondere alle interrupt()chiamate sul suo thread ... non tutte le chiamate "bloccanti" lo fanno, come ho cercato di sottolineare nella mia risposta. Le specifiche dell'attività che si sta tentando di interrompere fanno un'enorme differenza nell'approccio che deve essere utilizzato. Maggiori informazioni sull'attività sarebbero utili.
Erickson,

Se queste risposte non risolvono il problema, suppongo che ulteriori dettagli / codice dovrebbero aiutare a rispondere.
Elister,

Questi thread che vuoi limitare nel tempo; stanno effettuando chiamate bloccanti o sono in qualche ciclo in cui è possibile controllare facilmente alcune variabili per vedere se è il momento di uscire?
Scott Smith

Risposte:


376

Anzi piuttosto usa ExecutorServiceinvece 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<String> 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<String> {
    @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' timeoutargomento nel Future#get()metodo, ad esempio aumentalo a 5 e vedrai che il thread termina. È possibile intercettare il timeout nel catch (TimeoutException e)blocco.

Aggiornamento: per chiarire un malinteso concettuale, nonsleep() è necessario. È usato solo per scopi dimostrativi / SSCCE. Fai il tuo compito di lunga durata proprio lì al posto di . All'interno dell'attività di lunga durata, dovresti controllare se il thread non viene interrotto come segue:sleep()

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

24
Sostituisci Thread.sleep(4000)con qualche altra istruzione di lunga durata e l'esempio non funzionerà. In altre parole, questo esempio funzionerebbe solo se Taskprogettato per comprendere il Thread.isInterrupted()cambio di stato.
yegor256,

@BalusC Ho provato questo approccio cercando di terminare i miei thread, ma non sono riuscito a farlo funzionare. Puoi verificarlo qui: stackoverflow.com/questions/35553420/...
syfantid

Come viene gestito InterruptedException da future.cancel (true)?
bolei,

1
n un numero di persone ha commentato il nome del pacchetto, ed ecco un altro +1 per questo. È un'abilità così bella da assorbire. Grazie!
Ashwin Tumma,

@BalusC Ho un dubbio, se il futuro verrà eseguito in modo sincrono e se impiegherà più di un tempo predefinito, verrà chiuso. Altrimenti si eseguirà in futuro un po 'di tempo nel frattempo contiamo in tempo ... Grazie
Adeel Ahmad

49

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 di base come ExecutorServiceannullare le attività asincrone con interrupt()chiamate sul thread di lavoro. Quindi, ad esempio, se l'attività contiene una sorta di ciclo, dovresti controllare il suo stato di interruzione ad ogni iterazione. Se l'attività sta eseguendo operazioni di I / O, anche queste dovrebbero essere interrompibili, e configurarle può essere complicata. In ogni caso, tenere presente che il codice deve verificare attivamente la presenza di interruzioni; l'impostazione di un interrupt non fa necessariamente nulla.

Naturalmente, se l'attività è un semplice ciclo, puoi semplicemente controllare l'ora corrente ad ogni iterazione e rinunciare quando è trascorso un determinato timeout. In questo caso non è necessario un thread di lavoro.


Nella mia esperienza, l'unico codice che non reagisce per iniziare interrotto è il blocco nel codice nativo (in attesa del sistema operativo).
Thorbjørn Ravn Andersen,

@ ThorbjørnRavnAndersen Sono d'accordo, ma è un sacco di codice. Il mio punto è che non esiste un meccanismo generico per questo; devi capire la politica di interruzione dell'attività.
Erickson,

@erickson, sono d'accordo con il tuo. Alla risposta esatta, deve essere definita una politica di cancellazione per ogni attività, se sei interessato a fermarla in questo modo. Oppure il thread dovrebbe essere consapevole di cosa dovrebbe fare quando viene interrotto. Dopotutto, l'interruzione e l'arresto di qualsiasi thread è solo una richiesta che il thread di destinazione potrebbe accettare o rifiutare, quindi è meglio scrivere l'attività tenendo presente questo.
AKS

non è possibile eseguire il servizio di esecuzione del task sul thread chiamante? anche il responsabile della manutenzione può scegliere di eseguire l'attività in futuro?
filthy_wizard,

@ user1232726 Il execute()metodo dell'interfaccia padre, Executorpuò eseguire un'attività nel thread chiamante. Non esiste un'affermazione simile per i submit()metodi di ExecutorServicetali Futureistanze di ritorno . L'implicazione del servizio è che ci sono thread di lavoro che devono essere ripuliti tramite l'arresto e che le attività vengono eseguite in modo asincrono. Detto questo, non c'è nulla nel contratto che afferma che ExecutorServiceè vietato eseguire attività nel thread di invio; tali garanzie provengono dalle API di implementazione, come le Executorsfabbriche.
Erickson,

13

Prendi in considerazione l'utilizzo di un'istanza di ExecutorService . Entrambi invokeAll()e invokeAny()metodi sono disponibili con atimeout parametro.

Il thread corrente si bloccherà fino al completamento del metodo (non sono sicuro che sia desiderabile) o perché l'attività è stata completata normalmente o è stato raggiunto il timeout. È possibile ispezionare i resi Futureper determinare cosa è successo.


9

Supponendo che il codice thread sia fuori dal tuo controllo:

Dalla documentazione Java di cui sopra:

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

In alcuni casi, è possibile utilizzare trucchi specifici dell'applicazione. Ad esempio, se un thread è in attesa su un socket noto, è possibile chiudere il socket per restituire immediatamente il thread. 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 risponderebbe nemmeno a Thread.stop. Tali casi includono attacchi deliberati di negazione del servizio 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, altrimenti hai bisogno di una conoscenza specifica del thread, come avere un flag da impostare. Forse puoi richiedere che l'attività ti venga assegnata insieme al codice necessario per interromperla: definisci un'interfaccia con un stop()metodo. Puoi anche avvisare quando non riesci a interrompere un'attività.


8

BalusC ha detto:

Aggiornamento: per chiarire un malinteso concettuale, lo sleep () non è richiesto. È usato solo per scopi dimostrativi / SSCCE. Fai semplicemente il tuo compito da molto tempo lì al posto del sonno ().

Ma se lo sostituisci Thread.sleep(4000);con for (int i = 0; i < 5E8; i++) {}allora non si compila, perché il ciclo vuoto non genera un InterruptedException.

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

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

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


Il modo in cui lo faccio è quello di aggiungere una "eccezione Eccezione" nel metodo pubblico <T> call {} metodo
Roberto Linares

5

Penso che dovresti dare un'occhiata ai meccanismi di gestione della concorrenza adeguati (i thread in esecuzione in loop infiniti non suona bene di per sé, a proposito). Assicurati di leggere qualcosa sull'argomento "uccidere" o "fermare" sull'argomento .

Quello che stai descrivendo, assomiglia molto a un "appuntamento", quindi potresti voler dare un'occhiata al CyclicBarrier .

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

Di solito raccomando due libri in quest'area: Programmazione concorrente in Java e Concorrenza in pratica Java .


5

Ho creato una classe di supporto proprio 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) {}  

3

Vi mando un pezzo di codice che mostra un modo per risolvere il problema. Ad esempio sto leggendo un file. È possibile 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


3

Ecco la mia classe helper davvero semplice da usare per eseguire o chiamare un pezzo di codice Java :-)

Questo si basa sull'ottima 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<Void>()
        {
            @Override
            public Void call() throws Exception
            {
                runnable.run();
                return null;
            }
        }, timeoutCallback, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, int timeoutInSeconds)
    {
        return call(callable, null, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        try
        {
            Future<T> 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;
    }

}

2

Il frammento seguente avvierà 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 continuerà per la sua strada allegra. Anche se l'operazione non può essere annullata facilmente, il thread padre non attenderà il termine del thread figlio.

ExecutorService executorService = getExecutorService();
Future<SomeClass> future = executorService.submit(new Callable<SomeClass>() {
    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, e.g. logging, throwing an exception
}

Il getExecutorService()metodo può essere implementato in diversi modi. Se non si hanno particolari requisiti, è possibile semplicemente richiedere il Executors.newCachedThreadPool()pool di thread senza limiti superiori sul numero di thread.


Quali sono le importazioni richieste? Cosa sono SomeClasse Future?
ADTC

2

Una cosa che non ho visto menzionato è che uccidere i fili è generalmente una cattiva idea. Esistono tecniche per rendere i metodi filettati facilmente gestibili , ma è diverso dall'uccidere un thread dopo un timeout.

Il rischio con ciò che stai suggerendo è che probabilmente non sai in quale stato si troverà il thread quando lo uccidi, quindi rischi di introdurre instabilità. Una soluzione migliore consiste nell'assicurarsi che il codice thread non si blocchi o risponda correttamente a una richiesta di interruzione.


Senza un contesto un'affermazione come la tua sembra troppo restrittiva. In ambito accademico ho molto spesso la necessità di testare qualcosa fino a un timeout, e quando si verifica ho semplicemente lasciato cadere tutto il calcolo e ho registrato che si è verificato il timeout. Probabilmente è raro nel settore, ma comunque ...
Alessandro S.

@AlessandroS: questo è un punto ragionevole, anche se l'OP ha chiesto "soluzioni migliori", con cui ho inteso che la robustezza e l'affidabilità erano preferite rispetto alla forza bruta.
Dan Puzey,

2

Ottima risposta di BalusC:

ma solo per aggiungere che il timeout stesso non interrompe il thread stesso. anche se stai controllando mentre while (! Thread.interrupted ()) nella tua attività. se vuoi assicurarti che il thread sia fermo, dovresti anche assicurarti che future.cancel () sia invocato quando viene rilevata un'eccezione di timeout.

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<String> 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<String> {
    @Override 
    public String call() throws Exception {
      while(!Thread.currentThread.isInterrupted()){
          System.out.println("Im still running baby!!");
      }          
    } 
} 

0

Penso che la risposta dipenda principalmente dal compito stesso.

  • Sta facendo un compito ancora e ancora?
  • È necessario che il timeout interrompa un'attività attualmente in esecuzione immediatamente dopo la sua scadenza?

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

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, ti preghiamo di restringere i tuoi requisiti o mostrare un po 'di codice.


0

Stavo cercando un ExecutorService in grado di interrompere tutti i Runnable scaduti da esso eseguiti, ma non ne ho trovato nessuno. Dopo poche ore ne ho creato uno come di seguito. Questa classe 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<Runnable>(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();
}

0

Ora, incontro un problema come questo. Capita di decodificare l'immagine. Il processo di decodifica impiega troppo tempo a mantenere nero lo schermo. Aggiungo un controllo del tempo: quando il tempo è troppo lungo, quindi viene visualizzato dal thread corrente. Quanto segue è il diff:

   ExecutorService executor = Executors.newSingleThreadExecutor();
   Future<Bitmap> future = executor.submit(new Callable<Bitmap>() {
       @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);

0

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 viene eseguito entro il limite di tempo. il processo termina e genera 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
    }

0

Nella soluzione fornita da BalusC , il thread principale rimarrà bloccato per il periodo di timeout. Se si dispone di un pool di thread con più thread, sarà necessario lo stesso numero di thread aggiuntivo che utilizzerà la chiamata di blocco Future.get (timeout lungo, unità TimeUnit) per attendere e chiudere il thread se supera il periodo di timeout.

Una soluzione generica a questo problema è quella di creare un Decoratore ThreadPoolExecutor in grado di aggiungere la funzionalità di timeout. Questa classe Decorator dovrebbe creare tutti i thread di ThreadPoolExecutor e tutti questi thread dovrebbero essere utilizzati solo per attendere e chiudere ThreadPoolExecutor.

La classe generica dovrebbe essere implementata come di seguito:

import java.util.List;
import java.util.concurrent.*;

public class TimeoutThreadPoolDecorator extends ThreadPoolExecutor {


    private final ThreadPoolExecutor commandThreadpool;
    private final long timeout;
    private final TimeUnit unit;

    public TimeoutThreadPoolDecorator(ThreadPoolExecutor threadpool,
                                      long timeout,
                                      TimeUnit unit ){
        super(  threadpool.getCorePoolSize(),
                threadpool.getMaximumPoolSize(),
                threadpool.getKeepAliveTime(TimeUnit.MILLISECONDS),
                TimeUnit.MILLISECONDS,
                threadpool.getQueue());

        this.commandThreadpool = threadpool;
        this.timeout=timeout;
        this.unit=unit;
    }

    @Override
    public void execute(Runnable command) {
        super.execute(() -> {
            Future<?> future = commandThreadpool.submit(command);
            try {
                future.get(timeout, unit);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (ExecutionException | TimeoutException e) {
                throw new RejectedExecutionException(e);
            } finally {
                future.cancel(true);
            }
        });
    }

    @Override
    public void setCorePoolSize(int corePoolSize) {
        super.setCorePoolSize(corePoolSize);
        commandThreadpool.setCorePoolSize(corePoolSize);
    }

    @Override
    public void setThreadFactory(ThreadFactory threadFactory) {
        super.setThreadFactory(threadFactory);
        commandThreadpool.setThreadFactory(threadFactory);
    }

    @Override
    public void setMaximumPoolSize(int maximumPoolSize) {
        super.setMaximumPoolSize(maximumPoolSize);
        commandThreadpool.setMaximumPoolSize(maximumPoolSize);
    }

    @Override
    public void setKeepAliveTime(long time, TimeUnit unit) {
        super.setKeepAliveTime(time, unit);
        commandThreadpool.setKeepAliveTime(time, unit);
    }

    @Override
    public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
        super.setRejectedExecutionHandler(handler);
        commandThreadpool.setRejectedExecutionHandler(handler);
    }

    @Override
    public List<Runnable> shutdownNow() {
        List<Runnable> taskList = super.shutdownNow();
        taskList.addAll(commandThreadpool.shutdownNow());
        return taskList;
    }

    @Override
    public void shutdown() {
        super.shutdown();
        commandThreadpool.shutdown();
    }
}

Il decoratore sopra può essere usato come di seguito:

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args){

        long timeout = 2000;

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(true));

        threadPool = new TimeoutThreadPoolDecorator( threadPool ,
                timeout,
                TimeUnit.MILLISECONDS);


        threadPool.execute(command(1000));
        threadPool.execute(command(1500));
        threadPool.execute(command(2100));
        threadPool.execute(command(2001));

        while(threadPool.getActiveCount()>0);
        threadPool.shutdown();


    }

    private static Runnable command(int i) {

        return () -> {
            System.out.println("Running Thread:"+Thread.currentThread().getName());
            System.out.println("Starting command with sleep:"+i);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                System.out.println("Thread "+Thread.currentThread().getName()+" with sleep of "+i+" is Interrupted!!!");
                return;
            }
            System.out.println("Completing Thread "+Thread.currentThread().getName()+" after sleep of "+i);
        };

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