为什么在Java EE容器中不鼓励生成线程?

126

我学到的关于Java EE开发的最初的事情之一就是不应该在Java EE容器内部创建自己的线程。但当我思考时,我并不知道原因。

你能清楚地解释为什么这是不被鼓励的吗?

我确定大多数企业应用程序需要一些异步作业,比如邮件守护进程、空闲会话、清理作业等。

所以,如果确实不应该创建线程,那么在需要时应该采用什么正确的方式呢?


4
通常使用JMS消息和MDB执行异步任务。 - Ken Liu
5
一旦容器中实现了JSR 236,这个问题很快就会成为过去式。 - letmaik
5
这种做法原本被反对,因为任何第二个线程都应该由容器创建和管理,以便该线程可以访问其他企业资源。使用Java EE7,有一种标准且正确的方法来在企业环境中创建线程。通过使用并发工具包,您确保新线程是由容器创建和管理的,从而保证所有EE服务都可用。 - Chris Ritchie
在JSF/EJB的角度看,可以在这里找到几种正确的方法:https://dev59.com/lm025IYBdhLWcg3wNTAV - BalusC
9个回答

85
不建议这样做,因为环境中的所有资源都应该由服务器进行管理和可能的监控。此外,线程使用的大部分上下文通常与执行线程本身相关联。如果您只是启动自己的线程(我认为某些服务器甚至不允许),它无法访问其他资源。这意味着您无法获取InitialContext并进行JNDI查找以访问其他系统资源,例如JMS连接工厂和数据源。
有正确的方法可以实现此操作,但这取决于所使用的平台。 commonj WorkManager 对于 WebSphere 和 WebLogic 以及其他一些平台都是通用的 在此处了解更多信息 以及此处 此外,还有一个与今天早上这个问题 类似的问题。
更新:请注意,此问题和答案涉及2009年的Java EE状态,事情已经有所改善!

1
您无法获取InitialContext并进行JNDI查找以访问其他系统资源,例如JMS连接工厂和数据源。我有一个应用程序通过在启动线程时注入数据源来解决此问题,但我可能需要重新考虑这种方法... - rjohnston
6
现在使用核心Java EE API创建线程有一种标准且正确的方法。通过使用并发工具包,您可以确保新线程由容器创建和管理,从而保证所有EE服务可用。示例请见这里这里 - Chris Ritchie
@ChrisRitchie 感谢你的提示。如果只有 JBoss AS/IBM WAS 支持 Java EE 7 就好了... :-( - asgs
1
@asgs WildFly 8(JBoss AS的新名称)支持Java EE 7。IBM仅获得了Java EE 6认证[认证](http://www.oracle.com/technetwork/java/javaee/overview/compatibility-jsp-136984.html)。 - Chris Ritchie

34
对于EJB来说,使用线程同步原语来同步多个实例的执行不仅是不被推荐的,而且明确被规范禁止:

企业Bean不能使用线程同步原语来同步多个实例的执行。

并且

企业Bean必须避免管理线程。企业Bean不能尝试启动、停止、暂停或恢复线程,也不能更改线程的优先级或名称,并且不能尝试管理线程组。

原因是EJB旨在在分布式环境中运行。EJB可能会从集群中的一台机器移动到另一台机器。线程(以及套接字和其他受限制的设施)是这种可移植性的重要障碍。

3
Java EE7并发工具提供了在企业环境中创建线程的正确方式。示例请参见这里(http://blog.chris-ritchie.com/2013/09/simple-concurrency-example-with-wildfly.html)和这里(http://blog.chris-ritchie.com/2013/10/managed-thread-factory-example-in-wildfly.html)。 - Chris Ritchie
1
@Dan,你能解释一下为什么线程会成为将EJB从集群中的一台机器移动到另一台机器时重要的障碍吗? - Geek

13
你不应该自己创建线程的原因是这些线程将不会被容器管理。容器负责许多新手开发人员难以想象的事情,例如线程池、集群和崩溃恢复等。如果你启动一个线程,可能会失去其中一些功能。此外,容器允许你重新启动应用程序而不影响它运行的JVM。如果有容器控制之外的线程,那么这将如何实现呢?
这就是为什么从J2EE 1.4计时器服务被引入的原因。详见这篇文章

2
JSR 236为Java EE 7及更高版本添加了支持生成线程的功能。请参见Chris Ritchie的此兄弟回答 - Basil Bourque

12

Java EE 的并发工具

现在,使用核心 Java EE API 创建线程的标准和正确方法是:

通过使用并发工具,您可以确保新线程由容器创建和管理,从而保证所有 EE 服务可用。

示例在此处


3

没有真正的理由不这样做。我在Web应用程序中使用 Quarz Spring,没有任何问题。同时并发框架java.util.concurrent也可以使用。如果你实现自己的线程处理,请将线程设置为 deamon或者使用自己的deamon线程组,这样容器可以随时卸载您的Web应用程序。

但是要小心,bean作用域sessionrequest在生成的线程中不起作用!此外,基于 ThreadLocal的其他代码不能直接工作,您需要自己将值传输到生成的线程中。


2

根据蓝图设计,Java EE容器禁止使用线程。请参考蓝图以获取更多信息。


2

您可以在部署描述符中指示容器始终启动某些内容。然后,这些内容可以执行您需要完成的任何维护任务。

请遵守规则。将来的某一天,您会为自己的决定感到高兴:)


1

我从未听说过这是不被鼓励的,除了它不容易正确实现这一事实。

这是相当低级别的编程,就像其他低级技术一样,你应该有一个很好的理由。大多数并发问题可以使用内置构造(如线程池)更有效地解决。


7
确实,按照规范这是被禁止的。 - Ken Liu

1

我发现的一个原因是,如果在EJB中生成一些线程,然后尝试让容器卸载或更新您的EJB,那么您将遇到问题。几乎总有另一种方法可以完成某件事,您不需要Thread,所以请拒绝。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接