问题描述
我有一个timer定期运行的timerTask,但有时会卡住(不会失败或给出任何异常)。
因此,此任务的下一个迭代不会因为上一个任务卡住而开始。
我希望任务完成:
- 一段时间后,要么超时(以便下次迭代开始)。
- 或者即使下一个迭代正在运行,也要开始下一个迭代,这会强制取消任何先前正在运行的任务。
下面是我的代码:
private static Timer timer = new Timer();
private static TimerTask timerTask = new TimerTask() {
@Override
public void run() {
try{
aSeparateMethodWhichGetsStuckOccasionally();
}catch (Exception exception){
logger.info(">>> Exception : " + exception);
}
}
};
public static void scheduleTask() {
initialDelay = 600000;
gap = 600000;
timer.scheduleAtFixedRate(timerTask,initialDelay,gap);
}
解决方法
首先,必须编写aSeparateMethodWhichGetsStuckOccasionally()
,以便它可以正确响应中断。中断是停止许多操作的唯一方法。
如果该方法正在捕获InterruptedException
,请完全删除try / catch,然后将throws InterruptedException
添加到方法声明中。如果该方法包含任何catch (Exception)
子句,则必须检查InterruptedException
并在这种情况下终止,或者更好的是,更改子句以仅捕获您绝对需要捕获的异常,不包括InterruptedException。 (捕获Exception
是一个坏习惯。大多数未经检查的异常都存在,它们暴露了程序员的错误,应该纠正而不是隐藏它们。NullPointerException和IndexOutOfBoundsException是此类异常的示例。)
这将使中断该方法成为可能。然后,您可以使用ExecutorService.invokeAll对其执行超时:
private static final ExecutorService executor =
Executors.newSingleThreadExecutor();
public static TimerTask timerTask = new TimerTask() {
@Override
public void run() {
try {
Callable<Void> subtask = () -> {
aSeparateMethodWhichGetsStuckOccasionally();
return null;
};
List<Future<Void>> futures =
executor.invokeAll(Collections.singleton(subtask),gap,TimeUnit.MILLISECONDS);
Future<?> future = futures.get(0);
if (!future.isCancelled()) {
// Check if subtask threw an exception.
future.get();
}
} catch (Exception exception) {
logger.log(Level.INFO,">>> Exception: " + exception,exception);
}
}
};