Failing to call shutdown()
on a thread executor will result in a never terminating application.
Best practice to shut down the ExecutorService is this:
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.
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.
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.