Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Execute task repeatedly - until timeout occurs or condition is fulfilled - Synchronous execution

I am currently facing a specific problem, for which I am not sure how to tackle it. Let me briefly describe this problem:

  • In a method I have to call another method and process its response
  • If this response equals a certain value, I continue with the regular execution
  • Otherwise, I wait for x seconds (e.g. 2 seconds) and call the method again to process its response again
  • The last step is repeated until a certain period of time expired or the method delivers the intended response. Thus, in total I shall not wait forever but just e.g. at max 10 seconds to see if the method returns the intended response in that time.
  • Remark: If the method does not need 10 seconds to deliver the intended result, the regular execution should continue immediately after that. This means that I don't want to wait 10 seconds if the result is there after e.g. 2 seconds.

Just using "old-school" means I came up with a solution such as the following (partly pseudo-code to simplify)

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

However, using Java 8 (this is my restriction right now) I thought there might be other/better solutions for this?

As an alternative I came up with something using a ScheduledExecutorService e.g.:

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

For returning the ComplexValue I guess I need to use Callable instead of Runnable? Can I do it accordingly with Callable? Moreover, it always ran into the timeout even if the condition in the service execution was OK before. In this case I don't know whether this all is a bit too much "overhead" for implementing such a quite simple task. What are the benefits of such a solution versus plain Thread sleep?

Is there some Java 8 functionality that I am missing to implement this part?

To note: I don't have to execute different tasks in parallel in this loop. The execution of the main Method shall not continue until the timeout is expired or the desired result is there - thus, no async execution. The method in place needs to return some data based on the response from the service call.

like image 620
MDDCoder25 Avatar asked Feb 08 '19 08:02

MDDCoder25


People also ask

How to stop a thread after some time in Java?

First, we create a SteppedTask with four steps. Second, we run the task using a thread. Last, we interrupt the thread after ten seconds using a timer and a timeout task. With this design, we can ensure our long-running task can be interrupted while executing any step.

How to halt execution in Java?

To end a Java program, we can use the exit() method of the System class. It is the most popular way to end a program in Java. System. exit() terminates the Java Virtual Machine(JVM) that exits the current program that we are running.

How does Java Timer work?

Timer class provides a method call that is used by a thread to schedule a task, such as running a block of code after some regular instant of time. Each task may be scheduled to run once or for a repeated number of executions.


2 Answers

I'd use TimerTask instead. It repeats executing the "run" method every "waitTime" milliseconds. You can specify when to stop repeating this task by calling "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);
}
like image 115
Mohammed Alokshiya Avatar answered Oct 22 '22 20:10

Mohammed Alokshiya


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

This code will keep calling doSomething() until it returns the expected value or TIMEOUT_MS milliseconds have passed. Assuming that doSomething() returns quickly, it will never take more than TIMEOUT_MS (plus maybe a few milliseconds). Also, the inter-retry delay is constant and independent of the run time of doSomething() (will retry every 2 seconds even if doSomething() runs for 1.9 seconds), and the worst case time it takes is TIMEOUT_MS + the time one call to doSomething() takes.

It may be beneficial to implement the waiting/timeout logic in its own class Timeout. Then you can create an instance for a given timeout period and pass it on through different layers so that even the lowest layer (e.g. network IO) can set its timeout value appropriately to not exceed the deadline defined by a top layer of application logic.

Combine that Timeout class with polymorphic RetryStrategies and you have a very flexible way to specify and handle many different scenarios.

Using lambdas, you can even create a fully generic encapsulation of the full retry logic, like

<R> R retryForResult( Supplier<R> functionToCall, Function<R,Boolean> resultValidationFunction, long maxTimeMs, Iterable<Long> retryDelays );  
like image 42
JimmyB Avatar answered Oct 22 '22 20:10

JimmyB