How to properly shutdown executor services with Sp

2019-04-06 04:24发布

问题:

I have a command line application that uses a Spring-managed bean that's composed of a java ExecutorService created with:

ExecutorService service = Executors.newFixedThreadPool(4);

Now, I want my service to shutdown when my application shuts down, so I made my bean implement the DisposableBean interface and have a destroy method such as:

public void destroy(){
  service.shutdown();
}

Then I might be tempted to do something like register a shutdown hook on the Spring context. However I found out (the hard way, i.e., in a pre-production release) that this doesn't work: the shutdown hook doesn't get called before the ExecutorService.shutdown() method is called, causing a classic catch 22 problem (it does get called on interruption, i.e., if I hit Ctrl-C while the application is running). This escaped my unit tests because for some reason it seems to work fine from within JUnit, which is still puzzling me: what does JUnit do differently?

The solution I found so far is to explicitly call ApplicationContext.close() right before I exit my main function. I was wondering if there was a better solution to this and what are the best practices for having flexible thread pools managed by Spring. Also what if my bean is not directly managed by Spring but is created by a bean managed by Spring? Should I just cascade the calls to destroy()? Wouldn't this be very error prone?

I appreciate any comments, suggestions, further reading, RTFMs, magic recipes.

Thanks!

回答1:

Are you aware that this:

ExecutorService service = Executors.newFixedThreadPool(4);

can be replaced with this:

<bean id="service" class="java.util.concurrent.Executors" 
      factory-method="newFixedThreadPool" destroy-method="shutdown">
    <constructor-arg value="4"/>
</bean>

The spring context then manages, more directly, the shutdown of your executor service--and it can be more easily reused.



回答2:

Consider using Spring's TaskExecutor, which can be configured with a thread pool. http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html



回答3:

Per official Spring documentation, when using annotation-based configuration, for destroyMethod field of @Bean, Spring's default behavior is to automatically invoke public, no-arg methods named close or shutdown when the application context is being closed.

As a convenience to the user, the container will attempt to infer a destroy method against an object returned from the @Bean method. For example, given an @Bean method returning an Apache Commons DBCP BasicDataSource, the container will notice the close() method available on that object and automatically register it as the destroyMethod. This 'destroy method inference' is currently limited to detecting only public, no-arg methods named 'close' or 'shutdown'. The method may be declared at any level of the inheritance hierarchy and will be detected regardless of the return type of the @Bean method (i.e., detection occurs reflectively against the bean instance itself at creation time).

To re-iterate, this is the default behavior for annotation-driven configuration when a destroy method is not explicitly set. If this behavior is undesired explicitly setting destroy method to an empty string will disable this "feature":

To disable destroy method inference for a particular @Bean, specify an empty string as the value, e.g. @Bean(destroyMethod=""). Note that the DisposableBean and the Closeable/AutoCloseable interfaces will nevertheless get detected and the corresponding destroy/close method invoked.

On the other hand, when using XML configuration, this is not the default behavior... To achieve parity, destroy-method can be explicitly set to (inferred). Refer to the Destruction callbacks and Default initialization and destroy methods sections in the official docs for details.