I am currently facing a specific problem, for which I am not sure how to tackle it. Let me briefly describe this problem:
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.
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.
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.
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.
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);
}
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 );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With