8

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

然而,架构师提出了一些反对意见,指出在 web 应用程序中生成线程/拥有线程池是可怕的/禁止的/绝对的邪恶。

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

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

其次,热部署论点是否也适用于这种配置?

是的,我知道可以直接在 Tomcat 中声明一个工作池,然后通过 JNDI 将其注入 Spring。但是相比Spring提供的直接ThreadPoolTask​​Executor设施,还是有点麻烦

所以我的最后一个问题是:建筑师的反对意见与我的情况有关吗?

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

4

2 回答 2

11

邪恶有不同的层次,并不是所有的邪恶在每种情况下都算作邪恶。

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

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

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

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

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

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

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

于 2012-08-10T21:09:32.927 回答
2

恕我直言,在 Java EE 应用程序中拥有自我管理的 TaskExecutors 是不错的做法,如果它被正确隔离的话。将异步任务分离到一个孤立的实例中会引入新的复杂性、新的依赖性并降低性能。

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

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

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

于 2012-08-08T21:07:12.713 回答