Reputation: 199
I have a java code which I want to run. If the job does not complete within, say, 2 hours, then it should be killed automatically (basically, some kind of timed batch).
How to achieve this in Java?
Upvotes: 2
Views: 420
Reputation: 1610
If you are on Java 9 or higher, you can do the timeout batching as below:-
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(this::longRunningTask)
.orTimeout(2, TimeUnit.SECONDS);
future.get(); // j.u.c.ExecutionException after waiting for 2 second
If it completes within the timeout
limit, it will return the value (here an Integer
object in response to future.get()
method)
And, this batching is asynchronous (If you don't call get method explicitly.)
NOTE: This does not prevent the thread from completing the task, it just completes a future in main thread with a Timeout Exception so that main thread can continue. The background task/thread is still continues to finish. (look @Andreas comment)
Some Samples:-
final CompletableFuture<Void> future =
CompletableFuture.supplyAsync(this::longRunningTask)
.orTimeout(2, TimeUnit.SECONDS);
future.get(); // j.u.c.ExecutionException after waiting for 2 second
And the longRunningTask()
:-
private Void longRunningTask(){
log.info("Thread name" + Thread.currentThread().getName());
try {
log.info("Going to sleep for 10 sec...");
Thread.sleep(10*1000);
log.info("Sleep Completed. Task Completed.");
} catch (InterruptedException e) {
log.info("Exception Occurred");
}
finally{
log.info("Final Cleanup in progress.");
}
log.info("Finishing the long task.");
return null;
}
If you run above code, it will give Execution Exception in main thread (where future.get()
is called) but the longRunningTask
will still print Sleep Completed. Task Completed.
after completing 10 sec sleep.
If you carefully notice, the longRunnigThread
is never interrupted (does not enter in catch block) so continues normally, but main thread gets an exception on get()
.
Workaround/Solution:
Use ExecutorService
and submit the longRunnigTask future with this Exceutor
, if timeout occurs, shutdown the executor or else, shutdown after successful get()
in case of no timeout exception.
Sample:
ExecutorService myWorkers = Executors.newFixedThreadPool(1);
final CompletableFuture<Void> longTask =
CompletableFuture.supplyAsync(this::longRunningTask, myWorkers)
.orTimeout(2, TimeUnit.SECONDS);
try {
longTask.get();
} catch (InterruptedException | ExecutionException e) {
log.info("EE... Kill the executor thread/s.");
myWorkers.shutdownNow(); // this will interrupt the thread, catch the IntrExcep in thread and return the execution there
}
and the slightly modified longRunnigTask
private Void longRunningTask(){
log.info("Thread name" + Thread.currentThread().getName());
try {
log.info("Going to sleep for 10 sec...");
Thread.sleep(10*1000);
log.info("Sleep Completed. Task Completed.");
} catch (InterruptedException e) {
log.info("Exception Occurred");
return null; // this will finish the thread exce releasing all locking resources. Can be GCed then.
}
finally{
log.info("Final Cleanup in progress.");
}
log.info("Finishing the long task.");
return null;
}
With this approach, it won't complete the task inf timeout is occurred (you won't see Sleep completed. Task completed.
in logs..), and would see, exception occurred
in the longRunningTask
thread (because of interrupt caused by myWorker.shutdown
).
Upvotes: 4