Why does the ExecutorService interface not implement AutoCloseable?
Asked Answered
L

5

48

Failing to call shutdown() on a thread executor will result in a never terminating application.

Best practice to shut down the ExecutorService is this:

ExecutorService service = null;
try {
  service = Executors.newSingleThreadExecutor();
  // add tasks to thread executor
  …
} finally {
  if (service != null) service.shutdown();
}

Since Java knows the try-with-resources concept, wouldn't it be nice if we could do this?

try (service = Executors.newSingleThreadExecutor())
{
  // add tasks to thread executor
  …
} 
Lens answered 30/12, 2016 at 9:0 Comment(5)
Nice question though ... that thought never occurred to me ;-)Trivial
Related: #13883793Borehole
There are tons of "resourceful" non-Closeable APIs in the JDKHershelhershell
See this Answer with an important update for Java 19+.Sever
I had this question just now & searched in SO & got this!Brier
T
32

That ExecutorService has actually two shutdown-related methods; based on the simple fact that both ways of shutting down a service make sense.

Thus: how would you auto-close a service then? In a consistent manner that works for everybody?!

So, the reasonable explanation in my eyes: you can't make an ExecutorService a AutoClosable because that service does not have a single "close" like operation; but two!

And if you think you could make good use of such an auto-closing service, writing up your own implementation using "delegation" would be a 5 minute thing! Or probably 10 minutes, because you would create one version calling shutdown() as close operation; and one that does shutdownNow() instead.

Trivial answered 30/12, 2016 at 9:3 Comment(4)
You're probably right ;) What i really wanted to say is, that it is completely possible to wrap the functionality of ExecutorService into a class that supports AutoCloseableMikvah
Additional reasoning to not make ExecutorService AutoClosable in JDK - this is a service, not an a resourse... Exactly same reason for ExceturService to not extend Closeable interface.Vday
Thread pool is a valid resource. And choice between cleanup methods is hardly an excuse for not having a default cleanup methodAlesha
I don't think having 2 methods justifies not ensuring the resource as auto-cleanable. Its important when designing to do as much as we can to help the client code do the right thing.Brier
H
30

This is a mediocre workaround

ExecutorService service = Executors.newSingleThreadExecutor();
try (Closeable close = service::shutdown) {

}

Or, if the checked exception bothers you, you could write:

interface MyCloseable extends AutoCloseable {
    void close();
}

And then

ExecutorService service = Executors.newSingleThreadExecutor();
try (MyCloseable close = service::shutdown) {

}

Of course, you must never ever put anything between the assignment and the try statement, nor use the service local variable after the try statement.

Given the caveats, just use finally instead.

Hershelhershell answered 30/12, 2016 at 11:20 Comment(2)
Hi, this force me to declare also a } catch (IOException e) { e.printStackTrace(); } - how could I avoid that?Anther
@adrhc: Updated my answer with a possible workaroundHershelhershell
J
27

ExecutorService is AutoCloseable in Java 19+

Starting from Java 19, ExecutorService implements AutoCloseable.

The default implementation invokes shutdown() and waits for tasks to complete with awaitTermination in a loop. It calls shutdownNow() if interrupted.

Google Guava

For earlier Java, you can use Guava's ForwardingExecutorService to decorate ExeuctorService as AutoCloseable yourself:

class CloseableExecutorService extends ForwardingExecutorService implements AutoCloseable {

  private final ExecutorService delegate;

  CloseableExecutorService(ExecutorService delegate) {
    this.delegate = checkNotNull(delegate);
  }

  @Override
  protected ExecutorService delegate() {
    return delegate;
  }

  @Override
  public void close() {
    // copy paste from JDK 19 EA
    boolean terminated = isTerminated();
    if (!terminated) {
      shutdown();
      boolean interrupted = false;
      while (!terminated) {
        try {
          terminated = awaitTermination(1L, TimeUnit.DAYS);
        } catch (InterruptedException e) {
          if (!interrupted) {
            shutdownNow();
            interrupted = true;
          }
        }
      }
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
}
Jaymejaymee answered 17/5, 2022 at 1:0 Comment(1)
Thank you! I missed this one! I am gad they did it! Should have been done much sooner!Brier
C
2

I don't see where AutoCloseable is that useful for an Executor. try-with-resources is for things that can be initialized, used, and released within the scope of a method. This works great for things like files, network connections, jdbc resources, etc., where they get opened, used, and cleaned up quickly. But an executor, especially a threadpool, is something you want available for a long time period, probably over the lifetime of the application, and will tend to get injected into things like singleton services, which can have a method that the DI framework knows to call on application shutdown to clean up the executor. This usage pattern works fine without try-with-resources.

Also, a big motivator behind try-with-resources is making sure exceptions don't get masked. That's not a consideration so much with executors, all the exception-throwing will be happening in the tasks submitted to the executor, exception-masking is not an issue.

Chlorothiazide answered 30/12, 2016 at 18:1 Comment(1)
Everything is in the context of a single method: the main method of the program.Clitoris
M
0

JAVA EXAMPLE - ExecutorService is AutoCloseable from Java 19+

As mentioned in the official doc - here

Java example to implement this -

try(ExecutorService executorService = Executors.newFixedThreadPool(10)){
    //do stuff with you executor service
    executorService.shutdown();
    try {
           if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
              log.warn("Shutting Down Executor forcefully .... ");
              executorService.shutdownNow();
           }
    }catch (InterruptedException e) {
              executorService.shutdownNow();
              Thread.currentThread().interrupt();
    } finally {
              log.info("----------FINISHED PROCESSING--------------");
    }
}


Mystic answered 21/8 at 16:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.