简体   繁体   中英

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 ? ie are you creating those yourself ? I suspect somewhere in those a thread interrupt is being ignored eg

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM