2010-04-28 6 views
26

Gibt es eine Möglichkeit, einen untergeordneten Thread nach einem bestimmten Zeitlimit in Java zu beenden? Edit: Auch dieser bestimmte Thread kann im schlimmsten Fall blockiert werden (Thread wird verwendet, um auf eine Dateiänderung zu warten und blockiert, bis dieses Ereignis eintritt), also bin ich nicht sicher, dass interrupt() erfolgreich sein wird?Killing Thread nach einem bestimmten Zeitlimit in Java

+0

Was Sie warten verwenden für die Dateiänderung? Object.wait() oder etwas anderes? – mdma

+0

Eigentlich verwende ich jpathwatch, um nach Dateiänderungen zu suchen und speziell nach 'watchService.take()', das blockiert wird, bis eine Datei erstellt/gelöscht wurde. – Traker

Antwort

33

Verwenden Sie , um die Runnable auszuführen, überprüfen Sie die Methoden, in denen Sie das Zeitlimit angeben können. Z.B.

ExecutorService executor = Executors.newSingleThreadExecutor(); 
executor.invokeAll(Arrays.asList(new Task()), 10, TimeUnit.MINUTES); // Timeout of 10 minutes. 
executor.shutdown(); 

Hier Task natürlich implementiert Runnable.

+0

Gute Vorschläge, aber kann der ExecutorService den Thread auch dann abbrechen, wenn er blockiert ist? – Traker

+1

Es wird den Thread unterbrechen, ja. Wenn Sie es anhängen möchten, verwenden Sie 'ExecutorService # awaistTermination()', siehe auch das Beispiel in der Einführung von Javadoc. – BalusC

+14

IMHO, 'InvokeAll' ist Overkill. Benutze 'executor.submit (new Task()). Get (10, TimeUnit.MINUTES);' –

5

Warum nicht interrupt() es nach einer bestimmten Zeit? Ihr erstellter Thread muss in der Lage sein, einen InterruptedException richtig zu behandeln.

Weitere Informationen zum sauberen Herunterfahren von Threads finden Sie in diesem Artikel (http://www.javaspecialists.eu/archive/Issue056.html).

Siehe auch das Executor/Future-Framework, die nützliche Methoden zum Sammeln von Ergebnissen und/oder Beenden von Threads innerhalb bestimmter Zeitgrenzen bieten.

+1

Der Thread muss nur auf "InterruptedException" reagieren, wenn er einen Anruf tätigt, der ihn auslösen kann. Wenn es sich in einer Schleife dreht, sollte es sein eigenes unterbrochenes Flag mit 'Thread.isInterrupted()' (oder nur 'Thread.interrupted()' prüfen, was den aktuellen Thread impliziert). –

0

Das Töten eines Threads ist in der Regel eine schlechte Idee aus Gründen, die mit der API-Dokumentation für Thread verknüpft sind.

Wenn Sie auf das Töten eingestellt sind, verwenden Sie einen ganz neuen Prozess.

Sonst ist die übliche Sache, die Threadabfrage System.nanoTime zu haben, eine Fahne (mögliches volatile) abzufragen, eine "Giftpille" oder etwas dieser Art in einer Warteschlange.

+0

Das Töten eines Threads ist möglicherweise kritisch, wenn Sie nicht den Code besitzen, den dieser Thread ausführt, und Sie müssen sicherstellen, dass er tot ist. Manchmal müssen beide Codes in derselben virtuellen Adresse ausgeführt werden. –

+0

@Elazar Es ist fast sicher wichtiger, dass der Prozess stabil bleibt. –

0

Brian hat Recht, es zu unterbrechen ist sicherer, als den Thread "anzuhalten".
Was passiert, wenn der Thread während der Modifizierung auf ein Objekt einrastet und plötzlich gestoppt wird (wodurch die Sperre aufgehoben wird)? Sie bekommen seltsame Ergebnisse.

4

Nicht direkt; Ich denke, der einfachste Weg besteht darin,() an diesem Thread mit diesem Zeitlimit beizutreten und den Thread zu unterbrechen, wenn es bis zum Ende des Joins noch nicht fertig ist.

, So

Thread t = ... 
t.join(timelimit); 
if (t.isAlive) t.interrupt(); 

Hinweis Ich benutzte Interrupt anstatt es tatsächlich zu töten, es ist viel sicherer. Ich würde auch empfehlen, Executoren zu verwenden, anstatt Threads direkt zu manipulieren.

+0

Dies funktioniert nicht gut, wenn der andere Thread alle CPUs packt, so dass der Join erst nach einer zu großen Verzögerung _started_ wird. – mafu

+0

Ich denke, das sollte 'if (t.isAlive())' lesen – PHPirate

0

Verwenden Sie nicht destroy(), da dies keine Bereinigung durchführt.

Der einfachste Weg ist join() zu bedienen, wie

try { 
    thread.join(); 
} catch (InterruptedException e) {//log exception...} 

Sie eine ExecutorService nutzen könnten. Das wäre sehr sinnvoll, wenn mehrere Threads gleichzeitig ausgeführt werden. Wenn Sie neue Threads erstellen müssen, während andere Threads ausgeführt werden, können Sie dies mit einer BlockingQueue kombinieren.

Eine ThreadPoolExecutor (eine ExecutorService -Implementierung) kann ein BlockingQueue als Argument nehmen, und Sie können einfach neue Threads der Warteschlange hinzufügen. Wenn Sie fertig sind, beenden Sie einfach die ThreadPoolExecutor.

private BlockingQueue<Runnable> queue; 
... 
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, new Long(1000), 
       TimeUnit.MILLISECONDS, this.queue); 

Sie können alle der Warteschlange hinzugefügten Threads zählen. Wenn Sie denken, Sie sind fertig (die Warteschlange leer ist, vielleicht?) Einfach vergleichen Sie diese mit

if (issuedThreads == pool.getCompletedTaskCount()) { 
     pool.shutdown(); 
    } 

Wenn die beiden übereinstimmen, sind Sie fertig. Eine weitere Möglichkeit, den Pool zu beenden ist eine zweite in einer Schleife warten:

try { 
     while (!this.pool.awaitTermination(1000, TimeUnit.MILLISECONDS)); 
} catch (InterruptedException e) {//log exception...} 
1

Sie können AOP und eine @Timeable Anmerkung für Ihre Methode von jcabi-aspects verwenden (Ich bin ein Entwickler):

@Timeable(limit = 1, unit = TimeUnit.SECONDS) 
String load(String resource) { 
    // do something time consuming 
} 

Wenn das Zeitlimit erreicht ist, wird Ihr Thread interrupted() Flag auf true gesetzt und es ist Ihre Aufgabe, diese Situation korrekt zu behandeln und die Ausführung zu stoppen. Normalerweise wird es von Thread.sleep(..) getan.

0

Einige hilfreiche Änderungen als Teil JEP 266 in CompletableFuture seit Java eingeführt wurden 9. orTimeout Methode verwenden, denn jetzt ist es möglich, es zu schreiben:

CompletableFuture.runAsync(thread::run) 
    .orTimeout(30, TimeUnit.SECONDS) 
    .exceptionally(throwable -> { 
     log.error("An error occurred", throwable); 
     return null; 
    }); 

In Java 8, leider, sollten Sie verwenden etwas zusätzlichen Code. Hier ist ein Beispiel für eine Delegation Muster Nutzung mit Hilfe von Lombok:

import com.google.common.util.concurrent.ThreadFactoryBuilder; 
import java.time.Duration; 
import java.util.concurrent.CompletableFuture; 
import java.util.concurrent.Executors; 
import static java.util.concurrent.TimeUnit.MILLISECONDS; 
import java.util.concurrent.TimeoutException; 
import static lombok.AccessLevel.PRIVATE; 
import lombok.AllArgsConstructor; 
import lombok.experimental.Delegate; 

@AllArgsConstructor(access = PRIVATE) 
public class TimeoutableCompletableFuture<T> extends CompletableFuture<T> { 

    public static TimeoutableCompletableFuture<Void> runAsync(
      Runnable runnable) { 
     return new TimeoutableCompletableFuture<>(
       CompletableFuture.runAsync(runnable)); 
    } 

    @Delegate 
    private final CompletableFuture<T> baseFuture; 

    public TimeoutableCompletableFuture<T> orTimeout(Duration duration) { 
     final CompletableFuture<T> otherFuture = new CompletableFuture<>(); 
     Executors.newScheduledThreadPool(
       1, 
       new ThreadFactoryBuilder() 
       .setDaemon(true) 
       .setNameFormat("timeoutable-%d") 
       .build()) 
       .schedule(() -> { 
        TimeoutException ex = new TimeoutException(
          "Timeout after " + duration); 
        return otherFuture.completeExceptionally(ex); 
       }, duration.toMillis(), MILLISECONDS); 

     return new TimeoutableCompletableFuture<>(
       baseFuture.applyToEither(otherFuture, a -> a)); 
    } 
} 

Natürlich über den Code einfach nur als eine statische Factory-Methode neu geschrieben werden:

public static CompletableFuture<Void> runAsyncOrTimeout(
     Runnable runnable, long timeout, TimeUnit unit) { 

    CompletableFuture<Void> other = new CompletableFuture<>(); 
    Executors.newScheduledThreadPool(
      1, 
      new ThreadFactoryBuilder() 
      .setDaemon(true) 
      .setNameFormat("timeoutafter-%d") 
      .build()) 
      .schedule(() -> { 
       TimeoutException ex = new TimeoutException(
         "Timeout after " + timeout); 
       return other.completeExceptionally(ex); 
      }, timeout, unit); 
    return CompletableFuture.runAsync(runnable).applyToEither(other, a -> a); 
} 
Verwandte Themen