有没有办法在Java中指定的时间限制后终止子线程? 编辑:此特定线程也可能在最坏的情况下被阻止(线程用于等待文件修改并阻塞直到发生此事件),所以我不确定interrupt()是否会成功?
答案 0 :(得分:36)
利用ExecutorService
执行Callable
,检查可以指定超时的方法。 E.g。
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.invokeAll(Arrays.asList(new Task()), 10, TimeUnit.MINUTES); // Timeout of 10 minutes.
executor.shutdown();
此处Task
当然会实现Callable
。
答案 1 :(得分:5)
为什么不在特定时间之后interrupt()
呢?您的衍生线程必须能够正确处理InterruptedException
。
有关彻底关闭线程的详细信息,请参阅此文章(http://www.javaspecialists.eu/archive/Issue056.html)。
另请参阅Executor / Future框架,它提供了在特定时间限制内收集结果和/或终止线程的有用方法。
答案 2 :(得分:4)
不直接;我认为最简单的方法是使用该时间限制在该线程上加入(),如果在连接结束时没有完成,则中断该线程。
所以,
Thread t = ...
t.join(timelimit);
if (t.isAlive) t.interrupt();
注意我使用了中断而不是实际杀死它,它更安全。我还建议使用执行程序而不是直接操作线程。
答案 3 :(得分:2)
您可以从@Timeable
(我是开发人员)为您的方法使用AOP和jcabi-aspects注释:
@Timeable(limit = 1, unit = TimeUnit.SECONDS)
String load(String resource) {
// do something time consuming
}
当达到时间限制时,您的线程将interrupted()
标志设置为true
,您的工作就是正确处理这种情况并停止执行。通常由Thread.sleep(..)
完成。
答案 4 :(得分:2)
自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);
}
答案 5 :(得分:0)
对于与Thread
的API文档相关联的原因,杀死某个帖子通常是一个坏主意。
如果你死定了,请使用一个全新的过程。
否则通常的做法是让线程轮询System.nanoTime
,轮询一个(可能的volatile
)标志,排队“毒丸”或那种性质的东西。
答案 6 :(得分:0)
Brian是对的,打断它比“停止”线程更安全 如果线程在中间修改时锁定对象并突然停止(导致锁被释放)怎么办?你得到了奇怪的结果。
答案 7 :(得分:0)
不要使用destroy()
,因为它不会执行任何清理。
最直接的方法是使用join()
,例如
try {
thread.join();
} catch (InterruptedException e) {//log exception...}
您可以使用ExecutorService
。如果你有几个并发运行的线程,这将是很有意义的。如果您需要在其他线程运行时生成新线程,则可以将其与BlockingQueue
结合使用。
ThreadPoolExecutor
(ExecutorService
- 实现)可以将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...}