在Java EE Web应用程序中实现并发

5
我们正在创建一个Web应用程序,需要在几个业务场景下实现并发。这个应用程序将部署在Tomcat容器中。我知道在Web容器中创建用户定义的线程是一个坏主意,因此正在尝试探索我的选项。
以下是一些可供选择的选项:
1. 将我的多线程库用作JCA组件。我们不愿使用此方法,因为可能涉及学习曲线。
2. 我知道有可用的WorkManager API,但我猜想它没有被Tomcat实现,所以这个选项不可行。
3. 我进行了一些研究,发现CommonJ库推荐用于Tomcat。有人用过吗?
4. 此外,我看到有ManagedExecutorService可用,但我不确定如何使用它,它是否与WorkManager API(和commonJ库)不同?
如果您对这些选项有任何帮助,请告诉我。顺便说一句,由于部署环境的原因,使用JMS是不可行的。我倾向于使用第三和第四种方法,但我对它们不太了解。能否请有经验的人指导一下?
4个回答

4
由于您正在使用Tomcat,不用担心它,可以随意操作。Java EE的Servlet部分没有提到线程等内容,这主要在EJB部分中。
就线程管理而言,Tomcat本身并没有太多作为一个非常非侵入性的容器。
最好将您的线程绑定到ServletContextListener上,以便您可以关注应用程序生命周期,并在应用程序关闭时关闭您的线程,但除此之外,不要过度关注它,使用您喜欢的任何东西即可。
补充说明-
简单的事实是,Tomcat并不关心这个问题,也不是那么复杂。对于每个HTTP监听器,Tomcat都有一个线程池,这就是它的管理水平的尽头。例如,Tomcat不会从一个安静的HTTP监听器中取出线程并将其分配给繁忙的监听器。如果Tomcat真正关心如何创建线程,它将阻止您这样做--但它并没有这样做。
这意味着,HTTP上下文之外的线程管理完全落在您作为实现者的肩膀上。Java EE公开了这些类型的设施,接口读起来很棒。但简单的事实是,Java EE API文档所宣传的理论能力与现代实现的现实迥然不同,特别是在像Tomcat这样的低端系统上。
并不是贬低Tomcat。Tomcat是一款优秀的软件。但对于大多数用例,额外的管理功能并不是必需的。
使用JDK提供的设施设置自己的线程池,并使用自己的线程生命周期模型,可能会使您成功地完成您正在处理的任何项目。这真的不是什么大问题。

我不确定生成线程是一个好主意。如果Tomcat不知道这些线程,那么它如何控制呢?例如,如果线程正在运行,关闭容器将是一个问题,而且让我们不要忘记,由于这些线程在Tomcat的控制范围之外,它们将与Tomcat竞争内存。 - arya
Arya,Will是对的,管理自己的线程(相信我的痛苦),不要走EJB/JCA路线。你在Servlet容器中的事实并不改变控制线程池的需求。Executor服务可以轻松帮助你做到这一点。将执行器的生命周期与Servlet的生命周期绑定,可以保证初始化和关闭的清洁。 - Bruno Grieder
BGR,will,你们对ManagedExecutorService有什么了解吗?在这种情况下,使用它是否更合适? - arya
不熟悉这个。看起来像是带有监听器的ExecutorService。我可以看到它的用途,但并不是特别有趣。你需要解决的更大问题是确保你的线程可以被停止和管理,之后Service会为你完成大部分工作。 - Will Hartung
其实我不确定Tomcat是否实现了ManagedExecutorService,但这绝对是一个很棒的东西。我想我可以在Servlet上下文加载器中启动池,并关闭池,但我真的想知道这里是否有人有ManagedExecutorService或CommonJ库的经验。 - arya

1

有几个选项。无论是否存在容器限制,按需生成单独的线程几乎总是一个不好的主意。这并不是说在Servlet环境中这种方法行不通,但您可以潜在地创建的线程数量可能会完全失控。

最简单的解决方案是使用普通的Java SE线程池通过正常的执行器服务。在Servlet监听器中启动池,并通过某些静态变量提供对其的访问。虽然不是非常美观,但它能够完成工作。根据您的确切用例,这实际上可能是最佳解决方案(如果您的用例相当低级)。

另一个选择是将OpenEJB添加到您的war中,然后利用@Asynchronous注释。

还有另一种选择,就是认识到如果业务要求非常简单或低级,则通常使用Tomcat。这基本上是使用像Tomcat这样的裸骨东西的全部意义。一旦您发现自己需要添加(大量)库,您可能已经超出了Tomcat的范围,并且最好使用已经具有所需功能(在这种情况下是异步执行)的服务器。例如TomEE、GlassFish、Resin、JBoss AS、Geronimo等。


Arjan,实际上我们的服务器会承受很重的负载。并发用户不是太多,但有很多用户会持续与服务器进行一些长时间运行的对话(不一定是事务性对话)。你认为Tomcat是否足以满足我们的需求? - arya
我曾经为一个使用多个Tomcat集群来处理每个集群每月数千万页面浏览量的软件即服务提供商工作。很多人误认为Tomcat只适用于小项目,但这并不正确。 - Steven Benitez
浏览次数并不是我所说的简单或小的意思。如果代码很简单,就是指你不需要用户界面(UI)、对象关系映射(ORM)等等,那么基本上你的代码就是“底层”的。当然,底层的代码可以处理成千上万个浏览次数(我们的一个应用程序部分在Tomcat上运行,每秒服务器可以轻松处理6000个事务,但这些事务基本上只是接受HTTP请求,将数据放入传输队列,并最终批量持久化到数据库中,代码长度最多几千行)。 - Arjan Tijms

0

你的Web应用程序中的每个Servlet -Java EE基础组件,用于处理HTTP请求-都是单例的,每个请求在其自己独立的线程中运行,因此无需自己启动/停止用户生成的线程。你的Web容器 -在这种情况下是Tomcat- 管理所有这些东西。

除此之外,你需要在代码中考虑一些多线程处理方面的问题。例如,由于Servlet是单例的,并且为该类生成了许多线程,因此在这些组件中具有实例属性是一个坏主意。


Carlos,实际上我的问题完全不同。你所建议的是http过程的基本概念,我知道这一点。但有些情况下,用户会调用需要时间的业务流程,而这是通过线程完成的。http请求只会调用多线程进程...其余的进程将由线程并发执行...目前这些线程是用户定义的线程,我对此感到不舒服。你有什么想法可以消除使用这些用户定义的线程吗? - arya

0

我已经多次使用CommonJ,它的表现非常出色。可以通过ServletContextListener进行初始化和销毁。


谢谢你的回复,Christian。终于找到有CommonJ经验的人了。我的第一个问题是:当我使用CommonJ时,会创建线程,这些线程是否受Tomcat控制?我希望它非常可扩展……根据你的经验,你会推荐在中等负载下使用吗? - arya

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