如何使用Spring正确关闭执行程序服务?

时间:2013-08-29 16:19:04

标签: java multithreading spring junit executorservice

我有一个命令行应用程序,它使用由以下创建的java ExecutorService组成的Spring管理的bean:

ExecutorService service = Executors.newFixedThreadPool(4);

现在,我希望我的服务在我的应用程序关闭时关闭,所以我将我的bean实现了DisposableBean接口,并有一个destroy方法,如:

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

然后我可能会想要在Spring上下文中注册一个关闭钩子。然而,我发现(艰难的方式,即在预生产版本中)这不起作用:在调用ExecutorService.shutdown()方法之前不会调用shutdown钩子,导致经典的catch 22问题(它会在中断时调用,即,如果我在应用程序运行时按Ctrl-C)。这逃过了我的单元测试,因为出于某种原因,它似乎在JUnit中工作正常,这仍然令我感到困惑:JUnit的做法有何不同?

我到目前为止找到的解决方案是在退出main函数之前显式调用ApplicationContext.close()。我想知道是否有更好的解决方案,以及有什么是Spring管理的灵活线程池的最佳实践。另外如果我的bean是直接由Spring管理但是由Spring管理的bean创建的呢?我应该将调用级联到destroy()吗?这不是很容易出错吗?

我感谢任何评论,建议,进一步阅读,RTFM,魔术食谱。

谢谢!

4 个答案:

答案 0 :(得分:26)

你知道吗:

ExecutorService service = Executors.newFixedThreadPool(4);

可以替换为:

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

Spring语境然后更直接地管理您的执行程序服务的关闭 - 它可以更容易地重用。

答案 1 :(得分:1)

考虑使用Spring的TaskExecutor,它可以配置一个线程池。 http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html

答案 2 :(得分:1)

根据官方Spring文档,当使用基于注释的配置时,对于@Bean的{​​{3}}字段,Spring的默认行为是自动调用名为close或{的公共,无法方法{1}}关闭应用程序上下文时。

  

为方便用户,容器将尝试推断a   针对从@Bean方法返回的对象的destroy方法。对于   给定一个返回Apache Commons DBCP的@Bean方法   BasicDataSource,容器会注意到close()方法   在该对象上可用并自动将其注册为   与destroyMethod。这种“破坏方法推理”目前仅限于此   仅检测名为'close'或'shutdown'的公共,非arg方法。该   方法可以在继承层次结构的任何级别声明   无论@Bean方法的返回类型如何,都将被检测到   (即,检测反映在bean实例本身上   在创作时间。)

要重新迭代,当未明确设置destroy方法时,这是注释驱动的配置的默认行为。如果不希望这种行为明确地将destroy方法设置为空字符串将禁用此“功能”:

  

要禁用特定@Bean的destroy方法推断,请指定   空字符串作为值,例如@Bean(=了destroyMethod “”)。请注意   DisposableBean和Closeable / AutoCloseable接口将   然而,检测到相应的破坏/关闭方法   调用

另一方面,使用XML配置时,是默认行为...要实现奇偶校验,destroyMethod可以显式设置为shutdown。有关详细信息,请参阅官方文档中的destroy-methodDestruction callbacks部分。

答案 3 :(得分:0)

只想将基于配置的 ExecutorService bean 创建添加到 @Keith's answer

@Bean("fixedThreadPool")
public ExecutorService fixedThreadPool() {
    return Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}