簡體   English   中英

在Java中指定一些時間限制后殺死線程

[英]Killing thread after some specified time limit in Java

有沒有辦法在Java中指定的時間限制后殺死子線程? 編輯:此特定線程也可能在最壞的情況下被阻止(線程用於等待文件修改並阻塞直到發生此事件),所以我不確定interrupt()是否會成功?

使用ExecutorService執行Callable ,檢查可以指定超時的方法。 例如

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

這里的Task當然是實現了Callable

為什么不在特定時間后interrupt()它? 您生成的線程必須能夠正確處理InterruptedException

有關干凈地關閉線程的更多信息,請參閱此文章( http://www.javaspecialists.eu/archive/Issue056.html )。

另請參見Executor / Future框架,它提供了在特定時間限制內收集結果和/或終止線程的有用方法。

不直接; 我認為最簡單的方法是使用該時間限制在該線程上加入(),如果在連接結束時沒有完成,則中斷該線程。

所以,

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

注意我使用了中斷而不是實際殺死它,它更安全。 我還建議使用執行程序而不是直接操作線程。

自Java 9以來,在CompletableFuture作為JEP 266的一部分引入了一些有用的更改。使用orTimeout方法,現在可以像下面這樣編寫它:

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

不幸的是,在Java 8中,您應該使用一些額外的代碼。 以下是在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));
    }
}

當然,上面的代碼很容易被重寫為一個靜態的工廠方法:

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

您可以從jcabi-aspects (我是開發人員)為您的方法使用AOP和@Timeable注釋:

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

當達到時間限制時,您的線程將被interrupted()標志設置為true ,您的工作就是正確處理這種情況並停止執行。 通常它由Thread.sleep(..)

對於與Thread的API文檔相關聯的原因,殺死線程通常是一個壞主意。

如果你死定了,請使用一個全新的過程。

否則通常的事情是讓線程輪詢System.nanoTime ,輪詢一個(可能的volatile )標志,排隊“毒丸”或那種性質的東西。

布賴恩是對的,打斷它比“停止”線程更安全。
如果線程在中間修改時鎖定對象並突然停止(導致鎖被釋放)怎么辦? 你得到奇怪的結果。

不要使用destroy()因為它不執行任何清理。

最直接的方法是使用join() ,就像

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

您可以使用ExecutorService 如果你有幾個並發運行的線程,這將是很有意義的。 如果您需要在其他線程運行時生成新線程,則可以將其與BlockingQueue結合使用。

ThreadPoolExecutorExecutorService -implementation)可以將BlockingQueue作為參數,您只需將新線程添加到隊列中即可。 完成后,您只需終止ThreadPoolExecutor

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

您可以保留添加到隊列中的所有線程的計數。 當你認為你已經完成時(隊列是空的,或許?)只需將其與之比較即可

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

如果兩者匹配,你就完成了。 終止池的另一種方法是在循環中等待一秒:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM