在Java中指定一些时间限制后杀死线程

Tra*_*ker 32 java multithreading

有没有办法在Java中指定的时间限制后杀死子线程?编辑:此特定线程也可能在最坏的情况下被阻止(线程用于等待文件修改并阻塞直到发生此事件),所以我不确定interrupt()是否会成功?

Bal*_*usC 40

利用ExecutorService执行Callable,签出可以指定超时的方法.例如

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

Task当然是实现的Callable.

  • 恕我直言,`invokeAll`是矫枉过正的.只需使用`executor.submit(new Task()).get(10,TimeUnit.MINUTES);` (21认同)
  • @BalusC:`ExecutorService`文档表明`invokeAll`将阻止当前线程:"执行给定任务,返回一个Futures列表,其中包含所有完成或超时到期时的状态和结果,以先发生者为准." 另外我认为`Task`必须实现`Callable`而不是`Runnable`. (6认同)
  • @Peter:`Future#get()`阻止当前线程. (4认同)

ytt*_*rrr 6

一些有用的变化是引入的一部分JEP 266CompletableFuture从Java 9.使用orTimeout方法,现在,就可以直接写成:

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

不幸的是,在 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,上面的代码可以很容易地重写为静态工厂方法:

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);
}
Run Code Online (Sandbox Code Playgroud)


Bri*_*new 5

为什么不在interrupt()特定的时间之后呢?你的衍生线程必须能够InterruptedException正确处理.

有关干净地关闭线程的更多信息,请参阅此文章(http://www.javaspecialists.eu/archive/Issue056.html).

另请参见Executor/Future框架,它提供了在特定时间限制内收集结果和/或终止线程的有用方法.


Oak*_*Oak 5

不直接;我认为最简单的方法是在具有该时间限制的线程上 join() ,如果在 join 结束时还没有完成,则中断线程。

所以,

Thread t = ...
t.join(timelimit);
if (t.isAlive()) t.interrupt();
Run Code Online (Sandbox Code Playgroud)

注意我使用了中断而不是实际杀死它,它更安全。我还建议使用执行程序而不是直接操作线程。

  • 如果另一个线程占用了所有 CPU,这将无法正常工作,因此连接仅在延迟过大后 _started_ 。 (2认同)