Reputation: 9816
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!
Upvotes: 17
Views: 27947
Reputation: 4440
You can use @PreDestroy
annotation as well:
@Component
public class MyService {
private final ExecutorService executor = Executors.newCachedThreadPool();
@PreDestroy
@SneakyThrows
void shutdownExecutor() {
executor.shutdown();
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
}
}
Alternatively if the ExecutorService
itself is a bean:
@Bean(name = "fixedThreadPool", destroyMethod = "shutdown")
public ExecutorService fixedThreadPool() {
return Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
Upvotes: 1
Reputation: 7428
Just want to add the config based ExecutorService bean creation to @Keith's answer
@Bean("fixedThreadPool")
public ExecutorService fixedThreadPool() {
return Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
Upvotes: 1
Reputation: 9072
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.
Upvotes: 11
Reputation: 6889
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
Upvotes: 1
Reputation: 4184
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.
Upvotes: 27