Java执行任务有多次重试和超时

Hir*_*o2k 8 java timeout

我正在尝试创建一个在最长时间内执行给定任务的方法.如果在那段时间内未能完成,应该在放弃之前重试多次.它也应该在每次尝试之间等待几秒钟.这就是我想出来的,我想对我的方法提出一些批评.他们使用这个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;
}
Run Code Online (Sandbox Code Playgroud)

Sim*_*nni 5

我认为,但我认为,如果您正在安排与网络相关的任务,则不应重试,而应最终并行运行它们.我稍后会介绍其他方法.

关于您的代码,您应该将任务传递给执行程序,或者将FutureTask传递给线程.它不会产生一个线程或自己执行.如果你有一个执行程序(参见ExecutorService),你甚至不需要FutureTask,你可以简单地安排它并获得一个可调用的.

所以,假设你有一个ExecutorService,你可以调用:

Future<T> future = yourExecutor.submit(task);
Run Code Online (Sandbox Code Playgroud)

Future.get(timeout)将等待该超时并最终返回TimeoutException,即使该任务从未启动过,例如,如果Executor已经忙于完成其他工作而无法找到一个空闲线程.所以,你最终可能会尝试5次并等待几秒钟,而不会让任务有机会运行.这可能是也可能不是你所期望的,但通常不是.也许你应该等到它开始才能超时.

此外,即使它抛出TimeoutException,您也应该显式取消Future,否则它可能会继续运行,因为文档和代码都没有说当get with timeout失败时它会停止.

即使你取消它,除非Callable被"正确写入",它可能会继续运行一段时间.在这部分代码中你无法做到这一点,只要记住,没有任何线程可以"真正地停止"另一个线程在Java中所做的事情,并且有充分的理由.

但是,我认为您的任务主要与网络相关,因此它应该对线程中断做出正确的反应.

我通常使用不同的策略是这样的情况:

  1. 我会写公共静态T执行(可调用任务,int maxTries,int超时),所以任务,最大尝试次数(可能是1),最大总超时("我希望答案最多10秒,无论多少次你尝试,10秒或什么都没有")
  2. 我开始产生任务,将它交给执行者,然后调用future.get(timeout/tries)
  3. 如果我收到结果,请将其退回.如果我收到例外,将再试一次(见后文)
  4. 如果我得到超时,我不会取消未来,而是将其保存在列表中.
  5. 我检查是否已经过了太多时间,或者重试次数太多.在这种情况下,我取消列表中的所有期货并抛出异常,返回null,无论如何
  6. 否则,我循环,再次安排任务(与第一个任务并行).
  7. 见第2点
  8. 如果我没有收到结果,我会检查列表中的未来,也许是之前生成的任务之一.

假设您的任务可以多次执行(因为我认为它们是,否则无法重试),对于网络内容我发现此解决方案可以更好地工作.

假设您的网络实际上非常繁忙,您要求网络连接,每次重试20秒.由于您的网络繁忙,20次重试中没有一次能够在2秒内完成连接.但是,持续40秒的单次执行可能会设法连接和接收数据.这就像一个人在网络缓慢时强行按f5在页面上,它不会有任何好处,因为每次浏览器必须从头开始.

相反,我保持各种期货运行,第一个设法获取数据将返回结果,其他将停止.如果第一个挂起,第二个可以工作,或者第三个可能.

与浏览器相比,就像打开另一个选项卡并重试在那里加载页面而不关闭第一个选项卡.如果网络很慢,第二个将花费一些时间,但不会停止第一个,最终将正确加载.如果相反第一个标签挂起,第二个标签将快速加载.无论哪个先加载,我们都可以关闭另一个标签.