2012-08-01 160 views
8

我试图创建一个方法,可以在最长的时间内执行给定的任务。如果在这段时间内没有完成,在放弃之前应重试多次。它也应该在每次尝试之间等待几秒钟。这是我提出的,我想对我的方法提出一些批评。他们是使用ScheduledExecutorService来做到这一点的更简单的方法,还是我这样做的方式就够了吗?Java执行任务时执行了多次重试和超时

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; 
} 

回答

5

我想,但它是我的观点,如果你正在计划与网络相关的任务时,你不应该重试,但最终在并行运行。稍后我会介绍这种方法。

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

所以,因为你有一个ExecutorService,您可以拨打:

Future<T> future = yourExecutor.submit(task); 

的Future.get(超时)会等待超时,并最终与TimeoutException异常返回任务就算从来没有下开始的,例如,如果Executor已经忙于做其他工作并且找不到空闲线程。所以,你最终可能会尝试5次并等待几秒钟,而不会让任务有机会运行。这可能是也可能不是你期望的,但通常不是。也许你应该等待它开始,然后再给它一个超时时间。

此外,即使抛出TimeoutException,您也应该明确地取消Future,否则它可能会继续运行,因为当超时获取失败时,文档或代码都不会停止运行。

即使您取消它,除非Callable已被“正确书写”,它可能会持续运行一段时间。没有什么可以在这部分代码中做到这一点,只要记住没有线程可以“真正停止”另一个线程在Java中做什么,并且有很好的理由。

但是,我想你的任务将主要是网络相关的,所以它应该正确地对线程中断作出反应。

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

  1. 我会写公共静态牛逼执行(可调用任务,诠释maxTries,INT超时),所以任务,尝试的最大数量(可能1 ),最大总超时时间(“无论您尝试多少次,10秒或什么都不需要,我最多需要10秒钟的回答”)
  2. 我开始产卵任务,将其交给执行者,然后调用将来。获得(超时/尝试)
  3. 如果我收到结果,请将其退回。如果我收到例外情况,请再试一次(请参阅后面的内容)
  4. 如果我得到超时,我不会取消未来,而是将它保存在列表中。
  5. 我检查是否有太多的时间过去了,或者是太多的重试。在这种情况下,我取消列表中的所有期货并抛出异常,返回空值,无论如何
  6. 否则,我循环,再次安排任务(与第一个并行)。
  7. 看点二
  8. 如果我没有收到结果,我在列表中选中未来的(一个或多个),上一催生任务也许一个成功地做到这一点。

假设你的任务可以被执行多次(因为我想他们是,否则没有办法重试),对于网络的东西,我发现这个解决方案更好地工作。

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

相反,我保持各种期货运行,第一个管理获取数据将返回一个结果,其他将停止。如果第一个挂起,第二个挂起,或者第三个挂起。

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

1

调用execute的线程会阻塞很长时间。不知道这对你是否正确。基本上,对于这些类型的任务,ScheduledExecutorService是最好的。您可以安排任务并指定时间。看看ScheduledThreadPoolExecutor