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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11751329/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 06:13:57  来源:igfitidea点击:

Java execute task with a number of retries and a timeout

javatimeout

提问by Hiro2k

I'm trying to create a method that executes a given task in a maximum amount of time. If it fails to finish in that time, it should be retried a number of times before giving up. It should also wait a number of seconds between each try. Here's what I've come up with and I'd like some critiques on my approach. Is their a simpler way to do this using the ScheduledExecutorServiceor is my way of doing this suffice?

我正在尝试创建一种在最长时间内执行给定任务的方法。如果在那段时间内未能完成,则应在放弃之前重试多次。它也应该在每次尝试之间等待几秒钟。这是我的想法,我想对我的方法提出一些批评。他们是使用 的更简单的方法来做到这一点,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;
}

采纳答案by Simone Gianni

I think, but it's my opinion, that if you are scheduling network related tasks, you should not retry but eventually run them in parallel. I describe this other approach later.

我认为,但我的观点是,如果您正在安排与网络相关的任务,则不应重试,而应最终并行运行它们。稍后我将描述另一种方法。

Regarding your code, you should pass the task to an executor, or the FutureTask to a thread. It will not spawn a thread or execute by itself. If you have an executor (see ExecutorService), you don't even need a FutureTask, you can simply schedule it and obtain a callable.

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

So, given that you have an ExecutorService, you can call :

因此,鉴于您有一个 ExecutorService,您可以调用:

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

Future.get(timeout) will wait for that timeout and eventually return with TimeoutException even if the task has never started at all, for example if the Executor is already busy doing other work and cannot find a free thread. So, you could end up trying 5 times and waiting for seconds without ever giving the task a chance to run. This may or may not be what you expect, but usually it is not. Maybe you should wait for it to start before giving it a timeout.

Future.get(timeout) 将等待该超时并最终返回 TimeoutException 即使该任务根本没有开始,例如如果 Executor 已经忙于做其他工作并且找不到空闲线程。因此,您最终可能会尝试 5 次并等待几秒钟,而从未给任务机会运行。这可能是也可能不是您所期望的,但通常不是。也许您应该等待它启动,然后再给它超时。

Also, you should explicitly cancel the Future even if it throws TimeoutException, otherwise it may keep running, since nor documentation nor code says it will stop when a get with timeout fails.

此外,即使它抛出 TimeoutException,您也应该明确取消 Future,否则它可能会继续运行,因为文档和代码都没有说明它会在带有超时的 get 失败时停止。

Even if you cancel it, unless the Callable has been "properly written", it could keep running for some time. Nothing you can do it about it in this part of code, just keep in mind that no thread can "really stop" what another thread is doing in Java, and for good reasons.

即使您取消它,除非 Callable 已“正确编写”,否则它可能会继续运行一段时间。在这部分代码中,您无能为力,请记住,没有一个线程可以“真正停止”另一个线程在 Java 中所做的事情,并且有充分的理由。

However I suppose your tasks will mostly be network related, so it should react correctly to a thread interruption.

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

I usually use a different strategy is situations like this:

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

  1. I would write public static T execute(Callable task, int maxTries, int timeout), so the task, max number of tries (potentially 1), max total timeout ("I want an answer in max 10 seconds, no matter how many times you try, 10 seconds or nothing")
  2. I start spawning the task, giving it to an executor, and then call future.get(timeout/tries)
  3. If I receive a result, return it. If I receive an exception, will try again (see later)
  4. If however i get a timeout, I DON'T cancel the future, instead I save it in a list.
  5. I check if too much time has passed, or too many retries. In that case I cancel all the futures in the list and throw exception, return null, whatever
  6. Otherwise, I cycle, schedule the task again (in parallel with the first one).
  7. See point 2
  8. If I have not received a result, I check the future(s) in the list, maybe one of the previous spawned task managed to do it.
  1. 我会写 public static T execute(Callable task, int maxTries, int timeout),所以任务,最大尝试次数(可能是 1),最大总超时(“我想在最多 10 秒内得到答案,无论多少次你试试,10 秒或什么都没有”)
  2. 我开始生成任务,将其交给执行程序,然后调用 future.get(timeout/tries)
  3. 如果我收到结果,就返回它。如果我收到异常,会再试一次(见下文)
  4. 但是,如果我超时,我不会取消未来,而是将其保存在列表中。
  5. 我检查是否已经过去了太多时间,或重试次数是否过多。在这种情况下,我取消列表中的所有期货并抛出异常,返回 null,无论如何
  6. 否则,我会循环,再次安排任务(与第一个任务并行)。
  7. 见第 2 点
  8. 如果我还没有收到结果,我会检查列表中的未来,也许之前生成的任务之一设法做到了。

Assuming your tasks can be executed more than once (as I suppose they are, otherwise no way to retry), for network stuff I found this solution to work better.

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

Suppose your network is actually very busy, you ask for a network connection, giving 20 retries 2 seconds each. Since your network is busy, none of the 20 retries manages to get the connection in 2 seconds. However, a single execution lasting 40 seconds may manage to connect and receive data. It's like a person pressing f5 compulsively on a page when the net is slow, it will not do any good, since every time the browser has to start from the beginning.

假设您的网络实际上非常繁忙,您要求建立网络连接,每次重试 2 秒,重试 20 次。由于您的网络繁忙,20 次重试都无法在 2 秒内获得连接。但是,持续 40 秒的单次执行可能会设法连接和接收数据。这就像一个人在网速很慢的页面上强制按f5,没有任何好处,因为每次浏览器都必须从头开始。

Instead, I keep the various futures running, the first one that manages to get the data will return a result and the others will be stopped. If the first one hangs, the second one will work, or the third one maybe.

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

Comparing with a browser, is like opening another tab and retrying to load the page there without closing the first one. If the net is slow, the second one will take some time, but not stopping the first one, which will eventually load properly. If instead the first tab was hung, the second one will load rapidly. Whichever loads first, we can close the other tab.

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

回答by Bhaskar

The thread on which your executeis called will block for so much time. Not sure if this is correct for you. Basically , for these types of tasks , ScheduledExecutorServiceis best.You can schedule a task and specify the timings. Take a look at ScheduledThreadPoolExecutor

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