Tomcat webapp中的Spring ThreadPoolTask​​Executor - 糟糕的做法?

时间:2012-08-08 20:45:28

标签: spring tomcat threadpool

最近在我的项目中,我需要异步执行一些任务。由于我们在Tomcat中运行带有Spring的webapp,因此Spring提供的ThreadPoolTask​​Executor是一个解决方案。

然而,架构师提出了一些反对意见,声称它是可怕的/禁止的/绝对的邪恶来产生线程/在webapp中有一个线程池。

通过在网上和StackOverflow上搜索一下,我意识到是的,在 Java EE 容器中拥有自己的线程池是一种不好的做法。理由是,如果您拥有自己的线程池,则容器不会意识到它并且无法正确管理资源。当您需要对Web应用程序进行一些热部署时,这一点尤其重要。

现在,我们的用例是在Tomcat中运行的Spring webapp。首先,我们可以将Spring容器视为轻型 Java EE 容器吗?在这种情况下,Spring是直接管理线程池生命周期而不是应用程序本身,不是吗?

其次,热部署参数是否也适用于此配置?

是的,我知道可以直接在Tomcat中声明一个工作池,并通过JNDI将它注入Spring。但与Spring提供的直接ThreadPoolTask​​Executor工具相比,这有点麻烦

所以我的最后一个问题是:是我个案中建筑师的反对意见吗?

感谢您对此主题的建议和想法。

2 个答案:

答案 0 :(得分:10)

各种各样的邪恶程度,并不是所有情况在所有情况下都被视为邪恶。

按需创建线程而不是使用池通常被认为是邪恶的,但这不仅适用于Java EE,而且适用于几乎任何类型的服务器应用程序。

在Java EE中,EJB容器中特别不允许创建自己的线程。这是因为Java EE容器可能无形地将上下文数据存储在线程本地存储中,如果代码在其自己的线程中开始执行,则会丢失。

然而,Web容器没有这样的限制,根据规范,拥有线程池或多或少是合法的。这就是人们过去常常从EAR中的Web模块启动Quartz的原因,即使只使用了EJB模块,或者为什么Web模块中的代码可以将回调侦听器注册到非托管JMS队列,但是EJB不能这样做。

然而,在实践中,创建线程(通过池)实际上几乎总能工作,只要你记住,如果你使用例如EJB需要从在这些线程中运行的代码中的JNDI获取实例,并且不将EJB的引用传递给那些非托管线程。当然,您需要注意关闭池,但Java EE中的几乎所有类型的启动侦听器都有相应的关闭侦听器,您可以在其中执行此操作。

Java EE确实有一些官方方法可以减少创建自己的池的需要:

然而,一些算法需要单独的线程池,以防止死锁的可能性。由于没有任何Java EE解决方案能够绝对保证工作由不同的线程池完成,因此有时候除了创建自己的池之外没有其他合理的方法。

所以在最后一种情况下,将代码打开到死锁实际上比创建自己的线程池更加邪恶。

答案 1 :(得分:2)

Imho在Java EE应用程序中具有自我管理的TaskExecutors,如果它被正确隔离,则不是不好的做法。将异步任务分离到隔离的实例会引入新的复杂性,新的依赖性并降低性能。

未管理的架构参数当然是可空的,因为容器不知道/许多实例的所有者(例如静态引用),并且您可以在@Configuration类或Spring配置文件中配置执行器本身,至少使执行者本身由容器管理。

此外,Spring本身公开了几种执行方法的方法,例如使用@Scheduled注释(http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html)

热部署依赖性取决于您的工作队列的配置方式以及异步任务处理它们的方式。