我正在尝试创建一个在最长时间内执行给定任务的方法。如果在那段时间内未能完成,应该在放弃之前重试多次。它也应该在每次尝试之间等待几秒钟。这就是我想出来的,我想对我的方法提出一些批评。使用ScheduledExecutorService
这是一种更简单的方法吗?或者我这样做是否足够?
public static <T> T execute(Callable<T> task, int tries, int waitTimeSeconds, int timeout)
throws InterruptedException, TimeoutException, Exception {
Exception lastThrown = null;
for (int i = 0; i < tries; i++) {
try {
final Future<T> future = new FutureTask<T>(task);
return future.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException ex) {
lastThrown = ex;
} catch (ExecutionException ex) {
lastThrown = (Exception) ex.getCause();
}
Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSeconds));
}
if (lastThrown == null) {
lastThrown = new TimeoutException("Reached max tries without being caused by some exception. " + task.getClass());
}
throw lastThrown;
}
答案 0 :(得分:5)
我认为,但我认为,如果您正在安排与网络相关的任务,则不应重试,而应最终并行运行它们。我稍后会描述其他方法。
关于您的代码,您应该将任务传递给执行程序,或将FutureTask传递给线程。它不会产生一个线程或自己执行。如果你有一个执行者(参见ExecutorService),你甚至不需要FutureTask,你可以简单地安排它并获得一个可调用的。
因此,假设您有ExecutorService,则可以调用:
Future<T> future = yourExecutor.submit(task);
Future.get(timeout)将等待该超时并最终返回TimeoutException,即使该任务从未启动过,例如,如果Executor已经忙于完成其他工作而无法找到一个空闲线程。所以,你最终可能会尝试5次并等待几秒钟,而不会让任务有机会运行。这可能是也可能不是你所期望的,但通常不是。也许你应该等到它开始才能超时。
此外,即使它抛出TimeoutException,您也应该显式取消Future,否则它可能会继续运行,因为文档和代码都没有说当get with timeout失败时它会停止。
即使取消它,除非Callable被“正确写入”,否则它可能会继续运行一段时间。在这部分代码中你无法做到这一点,只要记住,没有任何线程可以“真正地停止”另一个线程在Java中所做的事情,并且有充分的理由。
但是我认为你的任务主要与网络有关,所以它应该对线程中断做出正确的反应。
我通常使用不同的策略是这样的情况:
假设您的任务可以被执行多次(因为我认为它们是,否则无法重试),对于网络内容我发现此解决方案可以更好地工作。
假设您的网络实际上非常繁忙,您要求网络连接,每次重试20秒。由于您的网络繁忙,20次重试中没有一次能够在2秒内完成连接。但是,持续40秒的单次执行可能会设法连接和接收数据。这就像一个人在网络缓慢的情况下强制性地在页面上按f5,它不会有任何好处,因为每次浏览器都必须从头开始。
相反,我保持各种期货运行,第一个设法获取数据将返回结果,其他将停止。如果第一个挂起,第二个可以工作,或者第三个可能。
与浏览器相比,就像打开另一个标签并重试在那里加载页面而不关闭第一个标签。如果网络很慢,第二个将花费一些时间,但不会停止第一个,最终将正确加载。如果相反第一个标签挂起,第二个标签将快速加载。无论哪个先加载,我们都可以关闭另一个标签。
答案 1 :(得分:1)
调用execute
的线程会阻塞这么长时间。不确定这对你是否正确。基本上,对于这些类型的任务,ScheduledExecutorService
是最好的。您可以安排任务并指定时间。看看ScheduledThreadPoolExecutor