So legen Sie eine Zeitüberschreitung für einen Thread fest


255

Ich möchte einen Thread für eine bestimmte Zeitspanne ausführen. Wenn es nicht innerhalb dieser Zeit abgeschlossen ist, möchte ich es entweder töten, eine Ausnahme auslösen oder auf irgendeine Weise damit umgehen. Wie geht das?

Eine Möglichkeit, wie ich aus diesem Thread herausgefunden habe, besteht darin, eine TimerTask innerhalb der run () -Methode des Threads zu verwenden.

Gibt es dafür bessere Lösungen?

 
EDIT: Hinzufügen eines Kopfgeldes, da ich eine klarere Antwort brauchte. Der unten angegebene ExecutorService-Code behebt mein Problem nicht. Warum sollte ich nach der Ausführung schlafen () (etwas Code - ich habe kein Handle über diesen Code)? Wenn der Code vollständig ist und sleep () unterbrochen wird, wie kann das ein timeOut sein?

Die Aufgabe, die ausgeführt werden muss, liegt nicht in meiner Kontrolle. Es kann ein beliebiger Code sein. Das Problem ist, dass dieser Code möglicherweise in eine Endlosschleife gerät. Ich möchte nicht, dass das passiert. Ich möchte diese Aufgabe also nur in einem separaten Thread ausführen. Der übergeordnete Thread muss warten, bis dieser Thread beendet ist, und den Status der Aufgabe kennen (dh ob das Zeitlimit überschritten wurde oder eine Ausnahme aufgetreten ist oder ob sie erfolgreich war). Wenn die Aufgabe in eine Endlosschleife gerät, wartet mein übergeordneter Thread unbegrenzt weiter, was keine ideale Situation ist.


EDIT: Hinzufügen eines Kopfgeldes, da ich eine klarere Antwort brauchte. Der unten angegebene ExecutorService-Code behebt mein Problem nicht. Warum sollte ich nach der Ausführung meines Codes schlafen ()? Wenn der Code vollständig ist und sleep () unterbrochen wird, wie kann das ein timeOut sein?
Java_geek

7
Das sleep()war nur ein Stich, um "lange laufende Aufgabe" darzustellen. Ersetzen Sie es einfach durch Ihre eigentliche Aufgabe;)
BalusC

1
... eine "lange laufende Aufgabe", die zufällig auf interrupt()Anrufe in ihrem Thread reagiert ... nicht alle "blockierenden" Anrufe, wie ich in meiner Antwort darauf hinweisen wollte. Die Besonderheiten der Aufgabe, die Sie abbrechen möchten, machen einen großen Unterschied in der Vorgehensweise, die verwendet werden sollte. Weitere Informationen zur Aufgabe wären hilfreich.
Erickson

Wenn diese Antworten das Problem nicht lösen, sollten weitere Details / Codes zur Beantwortung beitragen.
Elister

Diese Threads, die Sie zeitlich begrenzen möchten; Führen sie blockierende Anrufe durch oder befinden sie sich in einer Schleife, in der Sie leicht eine Variable überprüfen können, um festzustellen, ob es Zeit zum Beenden ist?
Scott Smith

Antworten:


376

In der Tat eher verwenden ExecutorServiceals Timer, hier ist eine 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!";
    }
}

Spielen Sie ein bisschen mit dem timeoutArgument in Future#get()method, z. B. erhöhen Sie es auf 5 und Sie werden sehen, dass der Thread beendet ist. Sie können das Timeout im catch (TimeoutException e)Block abfangen .

Update: Um ein konzeptionelles Missverständnis zu klären, sleep()ist das nicht erforderlich. Es wird nur für SSCCE / Demonstrationszwecke verwendet. Erledige einfach deine langjährige Aufgabe genau dort anstelle von sleep(). Innerhalb Ihrer lang laufenden Aufgabe sollten Sie überprüfen, ob der Thread nicht wie folgt unterbrochen wird :

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

24
Ersetzen Sie sie Thread.sleep(4000)durch eine andere Anweisung mit langer Laufzeit, und das Beispiel funktioniert nicht. Mit anderen Worten, dieses Beispiel würde nur funktionieren , wenn das Statusänderung Taskverstehen soll Thread.isInterrupted().
yegor256

@BalusC Ich habe diesen Ansatz versucht, um meine Threads zu beenden, konnte ihn aber nicht zum Laufen bringen. Sie können es hier überprüfen: stackoverflow.com/questions/35553420/…
syfantid

Wie wird InterruptedException von future.cancel (true) behandelt?
Bolei

1
n Die Anzahl der Personen hat den Paketnamen kommentiert, und hier ist noch eine +1 dafür. Das ist so eine schöne Fähigkeit, aufgenommen zu werden. Vielen Dank!
Ashwin Tumma

@BalusC Ich habe Zweifel, ob die Zukunft synchron ausgeführt wird und wenn sie länger als eine vordefinierte Zeit dauert, wird sie beendet. Sonst würde es in Zukunft irgendwann ausgeführt werden, während wir auf die Zeit zählen ... Danke
Adeel Ahmad

49

Es gibt keine 100% zuverlässige Möglichkeit, dies für eine alte Aufgabe zu tun. Die Aufgabe muss unter Berücksichtigung dieser Fähigkeit geschrieben werden.

Kern-Java-Bibliotheken wie das ExecutorServiceAbbrechen asynchroner Aufgaben mit interrupt()Aufrufen des Worker-Threads. Wenn die Aufgabe beispielsweise eine Art Schleife enthält, sollten Sie bei jeder Iteration den Interrupt-Status überprüfen . Wenn die Aufgabe E / A-Vorgänge ausführt, sollten diese ebenfalls unterbrechbar sein - und das Einrichten kann schwierig sein. Denken Sie in jedem Fall daran, dass der Code aktiv nach Interrupts suchen muss. Das Setzen eines Interrupts macht nicht unbedingt etwas.

Wenn es sich bei Ihrer Aufgabe um eine einfache Schleife handelt, können Sie bei jeder Iteration einfach die aktuelle Uhrzeit überprüfen und aufgeben, wenn eine bestimmte Zeitüberschreitung verstrichen ist. In diesem Fall wird kein Arbeitsthread benötigt.


Nach meiner Erfahrung ist der einzige Code, der nicht reagiert, um unterbrochen zu werden, das Blockieren des nativen Codes (Warten auf das Betriebssystem).
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen Ich stimme zu, aber das ist viel Code. Mein Punkt ist, dass es dafür keinen universellen Mechanismus gibt; Sie müssen die Unterbrechungsrichtlinie der Aufgabe verstehen.
Erickson

@erickson, ich stimme dir zu. Um auf den Punkt zu antworten: Für jede Aufgabe muss eine Stornierungsrichtlinie definiert sein, wenn Sie daran interessiert sind, sie auf diese Weise zu stoppen. Oder der Thread sollte wissen, was er tun soll, wenn er unterbrochen wird. Das Unterbrechen und Stoppen eines Threads ist schließlich nur eine Anforderung, die der Ziel-Thread möglicherweise akzeptiert oder ablehnt. Daher ist es besser, eine Aufgabe zu schreiben, die dies berücksichtigt.
AKS

Kann Executorservice nicht entscheiden, die Aufgabe auf dem aufrufenden Thread auszuführen? Auch der Executorservice kann sich dafür entscheiden, die Aufgabe irgendwann in der Zukunft auszuführen.
filthy_wizard

@ user1232726 Die execute()Methode der übergeordneten Schnittstelle führt Executormöglicherweise eine Aufgabe im aufrufenden Thread aus. Es gibt keine ähnliche Aussage für die submit()Methoden ExecutorServicedieser Rückgabeinstanzen Future. Der Service impliziert, dass es Arbeitsthreads gibt, die durch Herunterfahren bereinigt werden müssen, und dass Aufgaben asynchron ausgeführt werden. Der Vertrag enthält jedoch nichts, was besagt, dass die ExecutorServiceAusführung von Aufgaben im übergebenden Thread verboten ist. Diese Garantien stammen von den Implementierungs-APIs wie ExecutorsFabriken.
Erickson

13

Erwägen Sie die Verwendung einer Instanz von ExecutorService . Beide invokeAll()und invokeAny()Methoden sind mit einem timeoutParameter verfügbar .

Der aktuelle Thread wird blockiert, bis die Methode abgeschlossen ist (nicht sicher, ob dies wünschenswert ist), entweder weil die Aufgabe (n) normal abgeschlossen wurden oder das Zeitlimit erreicht wurde. Sie können die zurückgegebenen Informationen überprüfen, Futureum festzustellen, was passiert ist.


9

Angenommen, der Thread-Code liegt außerhalb Ihrer Kontrolle:

Aus der oben genannten Java- Dokumentation :

Was ist, wenn ein Thread nicht auf Thread.interrupt reagiert?

In einigen Fällen können Sie anwendungsspezifische Tricks verwenden. Wenn beispielsweise ein Thread auf einen bekannten Socket wartet, können Sie den Socket schließen, damit der Thread sofort zurückkehrt. Leider gibt es wirklich keine Technik, die im Allgemeinen funktioniert. Es sollte beachtet werden, dass in allen Situationen, in denen ein wartender Thread nicht auf Thread.interrupt reagiert, er auch nicht auf Thread.stop reagiert. Zu diesen Fällen gehören absichtliche Denial-of-Service-Angriffe und E / A-Vorgänge, für die thread.stop und thread.interrupt nicht ordnungsgemäß funktionieren.

Endeffekt:

Stellen Sie sicher, dass alle Threads unterbrochen werden können. Andernfalls benötigen Sie spezielle Kenntnisse über den Thread, z. B. ein Flag zum Setzen. Möglicherweise können Sie verlangen, dass Ihnen die Aufgabe zusammen mit dem Code zum Stoppen übergeben wird - definieren Sie eine Schnittstelle mit einer stop()Methode. Sie können auch warnen, wenn Sie eine Aufgabe nicht stoppen konnten.


8

BalusC sagte:

Update: Um ein konzeptionelles Missverständnis zu klären, ist sleep () nicht erforderlich. Es wird nur für SSCCE / Demonstrationszwecke verwendet. Erledige einfach deine langjährige Aufgabe genau dort anstelle von Schlaf ().

Aber wenn Sie ersetzen Thread.sleep(4000);mit for (int i = 0; i < 5E8; i++) {}dann ist es nicht kompilieren, weil die leere Schleife kein wirft InterruptedException.

Und damit der Thread unterbrechbar ist, muss er einen werfen InterruptedException.

Dies scheint mir ein ernstes Problem zu sein. Ich kann nicht sehen, wie ich diese Antwort anpassen kann, um mit einer allgemeinen langfristigen Aufgabe zu arbeiten.

Bearbeitet, um hinzuzufügen: Ich habe dies als neue Frage erneut beantwortet: [Unterbricht ein Thread nach einer festgelegten Zeit, muss er InterruptedException auslösen ? ]]


Die Art und Weise, wie ich es mache, ist das Hinzufügen einer "Throws Exception" in der öffentlichen Klasse <T> Aufruf {} Methode
Roberto Linares

5

Ich denke, Sie sollten sich die richtigen Mechanismen zur Behandlung von Parallelität ansehen (Threads, die in Endlosschleifen laufen, klingen an sich übrigens nicht gut). Stellen Sie sicher, dass Sie ein wenig über das Thema "Töten" oder "Stoppen" von Threads lesen .

Was Sie beschreiben, klingt sehr nach einem "Rendezvous", daher sollten Sie sich den CyclicBarrier ansehen .

Möglicherweise gibt es andere Konstrukte (wie zum Beispiel die Verwendung von CountDownLatch ), die Ihr Problem lösen können (ein Thread wartet mit einer Zeitüberschreitung auf den Latch, der andere sollte den Latch herunterzählen, wenn er seine Arbeit erledigt hat, wodurch Ihr erster Thread entweder danach freigegeben wird eine Zeitüberschreitung oder wenn der Latch-Countdown aufgerufen wird).

Normalerweise empfehle ich zwei Bücher in diesem Bereich: Concurrent Programming in Java und Java Concurrency in der Praxis .


5

Ich habe vor einiger Zeit eine Hilfsklasse dafür erstellt. Funktioniert super:

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

        }

    }
}

Es heißt so:

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

Ich poste Ihnen einen Code, der zeigt, wie Sie das Problem lösen können. Als Beispiel lese ich eine Datei. Sie können diese Methode für eine andere Operation verwenden, müssen jedoch die kill () -Methode implementieren, damit die Hauptoperation unterbrochen wird.

ich hoffe es hilft


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

Grüße


3

Hier ist meine wirklich einfach zu verwendende Hilfsklasse, um einen Teil des Java-Codes auszuführen oder aufzurufen :-)

Dies basiert auf der hervorragenden Antwort von 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

Das folgende Snippet startet eine Operation in einem separaten Thread und wartet dann bis zu 10 Sekunden, bis die Operation abgeschlossen ist. Wenn der Vorgang nicht rechtzeitig abgeschlossen wird, versucht der Code, den Vorgang abzubrechen, und setzt dann seinen fröhlichen Weg fort. Selbst wenn der Vorgang nicht einfach abgebrochen werden kann, wartet der übergeordnete Thread nicht auf das Beenden des untergeordneten Threads.

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
}

Die getExecutorService()Methode kann auf verschiedene Arten implementiert werden. Wenn Sie keine besonderen Anforderungen haben, können Sie einfach ein Executors.newCachedThreadPool()Thread-Pooling ohne Obergrenze für die Anzahl der Threads anfordern .


Was sind die erforderlichen Importe? Was sind SomeClassund Future?
ADTC

2

Eine Sache, die ich nicht erwähnt habe, ist, dass das Töten von Threads im Allgemeinen eine schlechte Idee ist. Es gibt Techniken, um Thread-Methoden sauber abzubrechen , aber das unterscheidet sich davon, einen Thread nach einer Zeitüberschreitung einfach zu .

Das Risiko bei dem, was Sie vorschlagen, besteht darin, dass Sie wahrscheinlich nicht wissen, in welchem ​​Zustand sich der Thread befindet, wenn Sie ihn beenden. Sie riskieren also, Instabilität einzuführen. Eine bessere Lösung besteht darin, sicherzustellen, dass sich Ihr Thread-Code entweder nicht selbst aufhängt oder auf eine Abbruchanforderung gut reagiert.


Ohne Kontext klingt eine Aussage wie Ihre zu restriktiv. Im akademischen Umfeld habe ich sehr oft die Notwendigkeit, etwas bis zu einem Timeout zu testen, und wenn es auftritt, lasse ich einfach alle Berechnungen fallen und zeichne auf, dass das Timeout aufgetreten ist. Wahrscheinlich ist es selten in der Branche, aber immer noch ...
Alessandro S.

@AlessandroS: Das ist ein vernünftiger Punkt, obwohl das OP nach "besseren Lösungen" fragte, womit ich meinte, dass Robustheit und Zuverlässigkeit gegenüber roher Gewalt bevorzugt wurden.
Dan Puzey

2

Tolle Antwort von BalusC's:

Aber nur um hinzuzufügen, dass das Timeout selbst den Thread selbst nicht unterbricht. auch wenn Sie mit while (! Thread.interrupted ()) in Ihrer Aufgabe prüfen. Wenn Sie sicherstellen möchten, dass der Thread gestoppt wird, sollten Sie auch sicherstellen, dass future.cancel () aufgerufen wird, wenn die Timeout-Ausnahme catch ist.

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

Ich denke, die Antwort hängt hauptsächlich von der Aufgabe selbst ab.

  • Erledigt es immer wieder eine Aufgabe?
  • Ist es notwendig, dass das Timeout eine aktuell ausgeführte Aufgabe sofort nach Ablauf unterbricht?

Wenn die erste Antwort Ja und die zweite Nein lautet, können Sie dies so einfach halten:

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();
    }
}

Wenn dies keine Option ist, schränken Sie bitte Ihre Anforderungen ein - oder zeigen Sie Code an.


0

Ich habe nach einem ExecutorService gesucht, der alle von ihm ausgeführten Runnables mit Zeitüberschreitung unterbrechen kann, aber keine gefunden hat. Nach ein paar Stunden habe ich eine wie unten erstellt. Diese Klasse kann geändert werden, um die Robustheit zu verbessern.

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();
    }
}

Verwendung:

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

Jetzt treffe ich ein Problem wie dieses. Es passiert das Bild zu dekodieren. Der Dekodierungsprozess dauert zu lange, als dass der Bildschirm schwarz bleibt. lch füge einen Zeitregler hinzu: Wenn die Zeit zu lang ist, öffne dich aus dem aktuellen Thread. Das Folgende ist der Unterschied:

   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

Ich hatte das gleiche Problem. Also habe ich mir eine einfache Lösung wie diese ausgedacht.

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

Garantiert, dass der Block nicht innerhalb des Zeitlimits ausgeführt wurde. Der Prozess wird beendet und eine Ausnahme ausgelöst.

Beispiel:

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

In der von BalusC angegebenen Lösung bleibt der Haupt-Thread für die Zeitüberschreitung blockiert. Wenn Sie einen Thread-Pool mit mehr als einem Thread haben, benötigen Sie dieselbe Anzahl zusätzlicher Threads, die Future.get verwenden (lange Zeitüberschreitung, TimeUnit-Einheit). Blockierungsaufruf , um zu warten und den Thread zu schließen, wenn der Timeout-Zeitraum überschritten wird.

Eine generische Lösung für dieses Problem besteht darin, einen ThreadPoolExecutor Decorator zu erstellen, der die Timeout-Funktionalität hinzufügen kann. Diese Decorator-Klasse sollte so viele Threads erstellen wie ThreadPoolExecutor, und alle diese Threads sollten nur zum Warten und Schließen des ThreadPoolExecutor verwendet werden.

Die generische Klasse sollte wie folgt implementiert werden:

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();
    }
}

Der obige Dekorateur kann wie folgt verwendet werden:

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

    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.