重复执行Java执行任务-直到发生超时或满足条件为止-同步执行

时间:2019-02-08 08:34:54

标签: java scheduled-tasks

我当前面临一个特定的问题,我不确定该如何解决。让我 简要描述这个问题:

  • 在一个方法中,我必须调用另一个方法并处理其响应
  • 如果此响应等于某个值,我将继续常规执行
  • 否则,我等待x秒(例如2秒),然后再次调用该方法以再次处理其响应
  • 重复最后一步,直到在一定时间段内结束或该方法传递了预期的响应。 因此,总的来说,我不会永远等待,而只是等待。在最多10秒的时间内查看该方法是否返回了预期的响应 时间。
  • 备注:如果该方法不需要10秒钟即可达到预期的结果,则常规 之后应立即执行。这意味着我不想等待10秒 如果结果在例如之后2秒。

仅使用“老式”意味着我想出了以下解决方案(为简化起见,部分伪代码)

//No exception handling to simplify method
public ComplexValuePart mainMethod(int id) {
    //Other code executed before

    int maxAmountTries = 5;
    int waitTime = 2000;

    int counter = 0;
    boolean conditionFulfilled = false;
    ComplexValue cv = null;
    while (counter++ < maxAmountTries && !conditionFulfilled) {
        cv = calculatingMethod(id);
        if (cv.conditionHolds()) {
            conditionFulfilled = true;
        } else {
            Thread.sleep(waitTime);
        }
    }

    if (counter == maxAmountTries && !conditionFulfilled) {
        //report error
    }

    //Continue processing with cv
    return doThingsWithCV(cv);
}

public ComplexValue calculatingMethod(int id) {
    //Implementation not relevant here
}

但是,使用Java 8(现在这是我的限制),我认为可能还有其他/更好的解决方案?

作为替代方案,我使用ScheduledExecutorService提出了一些建议,例如:

public void mainMethod(int id) {
    //Other code executed before
    ScheduledExecutorService service = Executors.newScheduledThreadPool(1);

    Future<?> future = service.scheduleAtFixedRate(new Runnable() {           
        @Override
        public void run() {
            ComplexValue cv = calculatingMethod(id);
            if (cv.conditionHolds()) {
                //shutdown service including awaitTermination
            }                
        }
    }, 0, 2, TimeUnit.SECONDS);

    try {
        future.get(20, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
        //shutdown service including awaitTermination
    }

    //Continue processing with cv - How can I access cv here?
}

要返回ComplexValue,我想我需要使用Callable而不是Runnable吗?我可以用Callable来做到这一点吗? 此外,即使之前服务执行中的条件确定,也总是会遇到超时问题。 在这种情况下,我不知道对于实现这样一个非常简单的任务而言,这是否有点“过多”? 与普通的Thread sleep相比,这种解决方案有什么好处?

实现该部分是否缺少一些Java 8功能?

注意:在此循环中,我不必并行执行不同的任务。主方法的执行不得继续 直到超时到期或所需的结果在那里-因此,不会执行async。 适当的方法需要根据服务调用的响应返回一些数据。

2 个答案:

答案 0 :(得分:2)

我改用TimerTask。它每“ waitTime”毫秒重复执行“ run”方法。您可以通过调用“ timer.cancel()”来指定何时停止重复此任务。

public void mainMethod(int id) {
    Timer timer = new Timer();

    timer.schedule(new TimerTask() {
        int counter = 0;
        @Override
        public void run() {
            cv = calculatingMethod(id);
            if (cv.conditionHolds() || counter++ > countLimit) {
                // conditionFulfilled = true;
                timer.cancel();
            }
        }
    }, 0, waitTime);
}

答案 1 :(得分:1)

final long deadline = System.currentTimeMillis() + TIMEOUT_MS;

boolean done;
Result result;
do {
  result = doSomething();
  done = resultIsOk(result);
  if ( !done ) {
    final long msRemaining = deadline - System.currentTimeMillis();
    if ( msRemaining > 0 ) {
      Thread.sleep(Math.min(msRemaining, RETRY_WAIT_TIME_MS);
    } else {
      done = true;
    }
  }
} while (!done);

if ( !resultIsOk(result) ) {
  // Error or something.
}

此代码将继续调用doSomething(),直到返回期望值 TIMEOUT_MS毫秒为止。假设doSomething()快速返回,则花费的时间永远不会超过TIMEOUT_MS(可能还有几毫秒)。另外,重试之间的延迟是恒定的,并且与doSomething()的运行时间无关(即使doSomething()运行1.9秒也会每2秒重试一次),而最坏的情况是{{ 1}}和一次致电TIMEOUT_MS所花费的时间。

在其自己的类doSomething()中实现等待/超时逻辑可能是有益的。然后,您可以为给定的超时时间创建一个实例,并将其传递到不同的层,这样,即使是最低层(例如,网络IO)也可以适当地设置其超时值,以不超过应用程序逻辑顶层定义的期限。 / p>

Timeout类与多态Timeout组合在一起,您可以使用非常灵活的方式来指定和处理许多不同的场景。

使用lambda,您甚至可以创建完全重试逻辑的完全通用封装,例如

RetryStrategies