How to properly shutdown java ExecutorService
I have a simple java ExecutorService
that runs some task objects (implements Callable
).
ExecutorService exec = Executors.newSingleThreadExecutor();
List<CallableTask> tasks = new ArrayList<>();
// ... create some tasks
for (CallableTask task : tasks) {
Future future = exec.submit(task);
result = (String) future.get(timeout, TimeUnit.SECONDS);
// TASKS load some classes and invoke their methods (they may create additional threads)
// ... catch interruptions and timeouts
}
exec.shutdownNow();
After all tasks are finished (either DONE or TIMEOUT-ed), I try to shutdown the executor, but it wont stop: exec.isTerminated() = FALSE.
I suspect that some tasks that are timeouted are not properly terminated.
And yes, I know that executor's shutdown is not guaranteing anything:
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via {@link Thread#interrupt}, so any task that fails to respond to interrupts may never terminate.
My question is, is there a way to ensure those (task) threads will terminate?
The best solution I came up with, is to call the System.exit()
at the end of my program, but that is plain silly.
Recommended way from Oracle API documentation page of ExecutorService:
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
If your pool is taking more time to shutdown, you can change
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
to
while (!pool.awaitTermination(60, TimeUnit.SECONDS))
A brief summary of the shutdown related methods
shutdown():
Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
shutdownNow():
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.
awaitTermination(long timeout, TimeUnit unit) throws InterruptedException:
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
Do you have control of those tasks ? i.e. are you creating those yourself ? I suspect somewhere in those a thread interrupt is being ignored e.g.
try {
....
}
catch {InterruptedException e) {
// do nothing
}
When an InterruptedException is thrown, the interrupt flag on the thread needs to be reset, otherwise the thread will not exit. See here for more info.
Unfortunately you may be using a library that doesn't obey this, in which case you can't easily circumvent this. In that case one heavyweight option is to fork off a sub process to perform the job of the Callable
, and that will clear up all resources upon process exit. Heavyweight and possibly non-trivial, but reliable.