jav*_*eek 246 java multithreading timeout timer
我想在一段固定的时间内运行一个线程.如果它没有在那段时间内完成,我想杀死它,抛出一些异常,或以某种方式处理它.怎么做到呢?
我从这个线程 中发现的一种方法是在Thread的run()方法中使用TimerTask.
有没有更好的解决方案呢?
编辑:添加赏金,因为我需要一个更清晰的答案.下面给出的ExecutorService代码没有解决我的问题.为什么我应该在执行后睡觉()(一些代码 - 我没有处理这段代码)?如果代码完成并且sleep()被中断,那怎么可能是timeOut?
需要执行的任务不在我的控制范围内.它可以是任何一段代码.问题是这段代码可能会遇到无限循环.我不希望这种情况发生.所以,我只想在一个单独的线程中运行该任务.父线程必须等到该线程完成并且需要知道任务的状态(即它是否超时或发生了一些异常或者是否成功).如果任务进入无限循环,我的父线程会无限期地等待,这不是一个理想的情况.
Bal*_*usC 361
事实上,而使用ExecutorService替代的Timer,这里是一个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!";
}
}
Run Code Online (Sandbox Code Playgroud)
使用方法中的timeout参数进行一些操作Future#get(),例如将其增加到5,您将看到线程完成.您可以拦截catch (TimeoutException e)块中的超时.
更新:澄清一个概念的误解,将sleep()是不必需的.它仅用于SSCCE /演示目的.只需在那里完成长期运行的任务sleep().在长时间运行的任务中,您应该检查线程是否未被中断,如下所示:
while (!Thread.interrupted()) {
// Do your long running task here.
}
Run Code Online (Sandbox Code Playgroud)
eri*_*son 46
对于任何旧任务,没有100%可靠的方法来执行此操作.任务必须考虑到这种能力.
核心Java库,例如通过工作线程上的调用ExecutorService取消异步任务interrupt().因此,例如,如果任务包含某种循环,则应在每次迭代时检查其中断状态.如果任务正在进行I/O操作,那么它们也应该是可中断的 - 并且设置它可能很棘手.无论如何,请记住代码必须主动检查中断; 设置中断不一定会做任何事情.
当然,如果你的任务是一个简单的循环,你可以只检查每次迭代的当前时间,并在指定的超时时间结束时放弃.在这种情况下不需要工作线程.
Dre*_*lls 13
考虑使用ExecutorService的实例.这两个invokeAll()和invokeAny()方法都可以用timeout参数.
当前线程将阻塞,直到方法完成(不确定是否需要),因为任务正常完成或达到超时.您可以检查返回的Future(s)以确定发生了什么.
假设线程代码不受您的控制:
从上面提到的Java 文档:
如果一个线程没有响应Thread.interrupt怎么办?
在某些情况下,您可以使用特定于应用程序的技巧.例如,如果线程正在等待已知套接字,则可以关闭套接字以使线程立即返回.不幸的是,确实没有任何技术可以发挥作用.应该注意的是,在等待线程没有响应Thread.interrupt的所有情况下,它也不会响应Thread.stop.此类情况包括故意拒绝服务攻击,以及thread.stop和thread.interrupt无法正常工作的I/O操作.
底线:
确保所有线程都可以被中断,否则您需要具体的线程知识 - 比如设置一个标志.也许您可以要求将任务与停止它所需的代码一起提供给您 - 使用stop()方法定义接口.您也可以在未能停止任务时发出警告.
BalusC说:
更新:为了澄清概念上的误解,不需要sleep().它仅用于SSCCE /演示目的.只是在那里做长期运行的任务而不是睡觉().
但是如果你Thread.sleep(4000);用for (int i = 0; i < 5E8; i++) {}它替换它就不会编译,因为空循环不会抛出InterruptedException.
并且为了使线程可以中断,它需要抛出一个InterruptedException.
这对我来说似乎是一个严重的问题.我看不出如何使这个答案适应一般长期运行的任务.
编辑添加:我将此作为一个新问题重新考虑:[ 在固定时间后中断线程,它是否必须抛出InterruptedException?]
我认为你应该看一下正确的并发处理机制(运行到无限循环的线程本身听起来不太好,顺便说一句).请务必阅读一些关于"查杀"或"停止"主题的内容.
你所描述的,听起来非常像"约会",所以你可能想看看CyclicBarrier.
可能有其他构造(例如使用CountDownLatch)可以解决您的问题(一个线程等待锁存器的超时,另一个应该倒计时锁定,如果它已经完成它的工作,这将释放您的第一个线程之后超时或调用锁存倒计时时).
我通常会推荐这方面的两本书:Java中的并发编程和实践中的Java并发.
小智 5
我刚刚为此创建了一个帮助类.效果很好:
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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
它被称为这样:
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) {}
Run Code Online (Sandbox Code Playgroud)