从Tomcat中的Servlet产生线程的推荐方法

问题描述 投票:48回答:6

可能重复一遍!我正在使用Tomcat作为服务器,并且想知道什么是在servlet中产生确定性结果的最佳方式。我正在从servlet操作中运行一些长期运行的更新,并希望请求完成并在后台进行更新。我认为我可以生成一个可以在后台运行并在自己的时间内完成的线程,而不是添加像RabbitMQ这样的消息传递中间件。我在其他SO线程中读到,服务器将终止服务器派生的线程,以使其很好地管理资源。

使用Tomcat时,是否有建议的方法来产生线程,后台作业。我还将Spring MVC用于该应用程序。

java multithreading tomcat servlets spring-mvc
6个回答
44
投票

您最安全的选择是使用具有最多线程数的应用程序级线程池,以便在必要时将任务排队。 ExecutorService在此方面非常有帮助。

在应用程序启动或Servlet初始化时,使用ExecutorService类:

Executors

然后在servlet的服务期间(对于您不感兴趣的情况,您可以忽略结果:]

executor = Executors.newFixedThreadPool(10); // Max 10 threads.

最后,在应用程序关闭或servlet销毁期间:

Future<ReturnType> result = executor.submit(new CallableTask());

30
投票

您可能使用像executor.shutdownNow(); // Returns list of undone tasks, for the case that. 这样的CommonJ WorkManager(JSR 237)实现:

CommonJ-JSR 237计时器和工作管理器

Foo-CommonJ是一个JSR 237计时器,WorkManager实施。 是设计用于容器中不随身携带实现–主要是普通的servletTomcat等容器。也可以是用于功能完善的Java EE应用程序没有WorkManager的服务器API或具有非标准API,例如JBoss。

为什么使用WorkManager?

常见的用例是一个Servlet或JSP需要从多个来源并将其显示在一页。 自己做线程像J2EE这样的托管环境容器不合适,应该永远不会在应用程序级别完成代码。在这种情况下,WorkManager API可用于检索中的数据平行。

安装/部署CommonJ

JNDI资源的部署供应商依赖。这个实现带有一个工厂类实现Foo-CommonJ与接口使其变得容易可部署于最受欢迎的容器。它也可以作为JBoss服务。更多...

更新:为了澄清,这是javax.naming.spi.ObjectFactory(看起来是JSR-236和JSR-237的后继者)写的关于非托管线程的内容:

2.1容器管理的线程与非托管的线程

Java EE应用程序服务器要求进行资源管理集中管理和保护应用程序组件免受消耗不必要的资源。这个可以通过汇集来实现资源和管理资源的生命周期。使用Java SE并发诸如Concurrency Utilities for Java EE PreviewAPI,java.util.concurrency和服务器中的java.lang.Thread应用程序组件,例如servlet或EJB 有问题,因为容器和服务器没有这些资源的知识

通过扩展java.util.TimerAPI,应用程序服务器和Java EE容器可以意识到使用并提供的资源适当的执行上下文异步操作与

一起运行

这主要是通过提供托管版本java.util.concurrent接口。

因此没有新的IMO,“旧”问题是相同的,非托管线程仍然是非托管线程:

  • 它们对于应用程序服务器是未知的,并且无权访问Java EE上下文信息。
  • 他们可以使用应用程序服务器背面的资源,并且没有任何管理能力来控制其数量和资源使用情况,这会影响应用程序服务器从故障中恢复资源或正常关闭资源的能力。

参考


7
投票

Spring通过Spring调度支持异步任务(在您的情况下,长时间运行)。建议不要将Java线程直接用于Quartz。

资源:


5
投票

我知道这是一个古老的问题,但是人们一直在问这个问题,一直尝试做这种事情(在处理servlet请求时显式地生成线程)……这是一种非常有缺陷的方法-不止一个原因...仅说明Java EE容器不赞成这种做法是不够的,尽管通常是正确的...

最重要的是,永远无法预测在任何给定时间servlet将接收多少并发请求。根据定义,Web应用程序(即Servlet)意味着能够一次处理给定端点上的多个请求。如果您正在编程,则要求处理逻辑显式启动一定数量的并发线程,则您将面临不可避免的情况,即耗尽可用线程并阻塞应用程序。您的任务执行器始终配置为与有限的合理大小的线程池一起使用。通常,它不大于10-20(您不希望有太多线程执行逻辑-取决于任务的性质,它们争用的资源,服务器上处理器的数量等)。 ,您的请求处理程序(例如MVC控制器方法)将调用一个或多个@Async批注的方法(在这种情况下,Spring将抽象任务执行器并使您变得容易)或明确使用任务执行器。代码执行时,它开始从池中获取可用线程。如果您始终一次处理一个请求,而没有立即的后续请求,那就很好。 (在那种情况下,您可能试图使用错误的技术来解决您的问题。)但是,如果该Web应用程序暴露于任意(甚至已知)客户端,这些客户端可能在向端点发送请求,那么您将快速耗尽线程池,请求将开始堆积,等待线程可用。仅出于这个原因,您应该意识到自己可能走错了路-如果您正在考虑采用这种设计。

更好的解决方案可能是stage要异步处理的数据(可以是队列,也可以是任何其他类型的临时/临时数据存储)并返回响应。有一个外部独立应用程序或什至有多个实例(部署在Web容器外部)轮询临时端点并在后台处理数据(可能使用有限数量的并发线程)。这样的解决方案不仅将为您提供异步/并发处理的优势,而且还可以扩展,因为您将能够根据需要运行任意数量的此类轮询器实例,并且可以将它们分配给临时端点。HTH


4
投票

严格来说,根据Java EE规范,您不能生成线程。如果同时出现多个请求,我还将考虑拒绝服务攻击(故意或其他方式)的可能性。

中间件解决方案肯定会更健壮且符合标准。


1
投票

从Spring 3开始,您可以使用@Async批注:

Spring reference: Chapter 23

与上下文文件中的@Service public class smg { ... @Async public getCounter() {...} } <context:component-scan base-package="ch/test/mytest">一起使用

请参阅本教程:<task:annotation-driven/>

在Tomcat7上对我非常有用,您不必管理线程池。

© www.soinside.com 2019 - 2024. All rights reserved.