【发布时间】:2016-12-14 01:47:29
【问题描述】:
在 java 类中,我有一个有时需要很长时间才能执行的方法。也许它挂在那个方法流中。我想要的是,如果该方法没有在特定时间完成,程序应该退出该方法并继续其余流程。
请告诉我有什么办法可以处理这种情况。
【问题讨论】:
标签: java
在 java 类中,我有一个有时需要很长时间才能执行的方法。也许它挂在那个方法流中。我想要的是,如果该方法没有在特定时间完成,程序应该退出该方法并继续其余流程。
请告诉我有什么办法可以处理这种情况。
【问题讨论】:
标签: java
您必须使用线程来实现这一点。线程是无害的 :) 下面的示例运行一段代码 10 秒然后结束它。
public class Test {
public static void main(String args[])
throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("0");
method();
}
});
thread.start();
long endTimeMillis = System.currentTimeMillis() + 10000;
while (thread.isAlive()) {
if (System.currentTimeMillis() > endTimeMillis) {
System.out.println("1");
break;
}
try {
System.out.println("2");
Thread.sleep(500);
}
catch (InterruptedException t) {}
}
}
static void method() {
long endTimeMillis = System.currentTimeMillis() + 10000;
while (true) {
// method logic
System.out.println("3");
if (System.currentTimeMillis() > endTimeMillis) {
// do some clean-up
System.out.println("4");
return;
}
}
}
}
【讨论】:
在不同的线程中执行方法,你可以随时结束一个线程。
【讨论】:
java.util.concurrent.*,尤其是FutureTask、Callable和Executors。请参阅此线程答案以获取示例:StackOverflow Question 240320。
基于上面的snipplet,我尝试创建一个美化的spring bean。
这样的执行器在有限的runtimeInMs中运行传递的limitedRuntimeTask。 如果任务在其时间限制内完成,调用者将继续正常执行。
如果 limitedRuntimeTask 未能在定义的 runtimeInMs 内完成, 调用者将收到返回的线程执行。如果定义了 timeBreachedTask, 它将在返回给调用者之前执行。
public class LimitedRuntimeExecutorImpl {
public void runTaskInLessThanGivenMs(int runtimeInMs, final Callable limitedRuntimeTask, final Callable timeBreachedTask) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
LOGGER.info("Started limitedRuntimeTask");
limitedRuntimeTask.call();
LOGGER.info("Finished limitedRuntimeTask in time");
} catch (Exception e) {
LOGGER.error("LimitedRuntimeTask exception", e);
}
}
});
thread.start();
long endTimeMillis = System.currentTimeMillis() + runtimeInMs;
while (thread.isAlive()) {
if (System.currentTimeMillis() > endTimeMillis) {
LOGGER.warn("LmitedRuntimeTask did not finish in time (" + runtimeInMs + ")ms. It will run in vain.");
if(timeBreachedTask != null ){
try {
LOGGER.info("Executing timeBreachedTask");
timeBreachedTask.call();
LOGGER.info("Finished timeBreachedTask");
} catch (Exception e) {
LOGGER.error("timeBreachedTask exception", e);
}
}
return;
}
try {
Thread.sleep(10);
}
catch (InterruptedException t) {}
}
}
}
【讨论】:
我觉得接受答案的方法有点过时了。使用Java8,它可以做得更简单。
说,你有方法
MyResult conjureResult(String param) throws MyException { ... }
那么你可以这样做(继续阅读,这只是为了展示方法):
private final ExecutorService timeoutExecutorService = Executors.newSingleThreadExecutor();
MyResult conjureResultWithTimeout(String param, int timeoutMs) throws Exception {
Future<MyResult> future = timeoutExecutorService.submit(() -> conjureResult(param));
return future.get(timeoutMs, TimeUnit.MILLISECONDS);
}
当然,抛出异常是不好的,这里是正确的扩展版本和正确的错误处理,但我建议你仔细检查它,你可能想做一些不同的事情(日志记录,在扩展结果中返回超时等):
private final ExecutorService timeoutExecutorService = Executors.newSingleThreadExecutor();
MyResult conjureResultWithTimeout(String param, int timeoutMs) throws MyException {
Future<MyResult> future = timeoutExecutorService.submit(() -> conjureResult(param));
try {
return future.get(timeoutMs, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
//something interrupted, probably your service is shutting down
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
//error happened while executing conjureResult() - handle it
if (e.getCause() instanceof MyException) {
throw (MyException)e.getCause();
} else {
throw new RuntimeException(e);
}
} catch (TimeoutException e) {
//timeout expired, you may want to do something else here
throw new RuntimeException(e);
}
}
【讨论】: