在Tomcat中从servlet生成线程的推荐方法是什么

时间:2010-09-19 13:46:45

标签: java multithreading tomcat servlets spring-mvc

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

使用Tomcat时是否有推荐的方法来生成线程,后台作业。我还使用Spring MVC作为应用程序。

6 个答案:

答案 0 :(得分:43)

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

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

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

然后在servlet服务期间(你可以忽略你不感兴趣的案例的结果):

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

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

executor.shutdownNow(); // Returns list of undone tasks, for the case that.

答案 1 :(得分:30)

您可以使用像Foo-CommonJ这样的CommonJ WorkManager(JSR 237)实现:

  

CommonJ - JSR 237计时器&amp;的WorkManager

     

Foo-CommonJ是一个JSR 237计时器和   WorkManager实现。 是的   设计用于容器   不要自己来   实现 - 主要是普通的servlet   容器如Tomcat 。它也可以   用于完全成熟的Java EE应用程序   没有WorkManager的服务器   API或具有非标准API   JBoss的。

     

为什么使用WorkManagers?

     

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

     

安装/部署CommonJ

     

部署JNDI资源   供应商依赖。这个实现   附带一个Factory类   实现   javax.naming.spi.ObjectFactory   界面使它变得容易   可部署在最受欢迎的   容器。它也可以作为   JBoss服务。更...

更新:为了澄清,这里是Concurrency Utilities for Java EE Preview(看起来这是JSR-236&amp; JSR-237的继承者)写的关于非托管线程的内容:

  

2.1容器管理与非管理线程

     

Java EE应用程序服务器   需要按顺序进行资源管理   集中管理和   保护应用程序组件   消耗不需要的资源。这个可以   通过汇集来实现   资源和管理资源   生命周期。使用Java SE并发   公用事业,如   java.util.concurrency API,   java.lang.Thread和   服务器中的java.util.Timer   应用程序组件,如   servlet或EJB是有问题的   容器和服务器没有   了解这些资源

     

通过扩展   java.util.concurrent API,   应用程序服务器和Java EE   容器可以意识到   使用和提供的资源   正确的执行上下文   使用运行的异步操作。

     

这主要是通过提供来实现的   托管版本的主导   java.util.concurrent.ExecutorService   接口

所以没有新的IMO,“旧”问题是一样的,非托管线程仍然是非托管线程:

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

参考

答案 2 :(得分:7)

Spring通过spring-scheduling支持异步任务(在你的情况下长时间运行)。我建议不要直接使用Java线程,而是使用Quartz。

<强>资源述略:

答案 3 :(得分:4)

严格地说,根据Java EE规范,不允许生成线程。如果有多个请求同时进入,我还会考虑拒绝服务攻击(故意或其他方式)的可能性。

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

答案 4 :(得分:4)

我知道这是一个老问题,但人们一直在问它,试图做这种事情(在处理servlet请求时显式产生线程)一直......这是一个非常有缺陷的方法 - 对于超过一个原因......简单地说Java EE容器对这种做法不满意是不够的,尽管通常都是正确的......

最重要的是,人们无法预测servlet在任何给定时间将接收多少并发请求。根据定义,Web应用程序,servlet意味着能够一次处理给定端点上的多个请求。如果您正在编程请求处理逻辑以显式启动一定数量的并发线程,那么您可能面临一个完全不可避免的情况,即可用线程耗尽并阻塞您的应用程序。您的任务执行程序始终配置为使用限制为有限合理大小的线程池。大多数情况下,它不大于10-20(你不需要太多线程执行你的逻辑 - 取决于任务的性质,他们竞争的资源,服务器上的处理器数量等)让我们说,您的请求处理程序(例如MVC控制器方法)调用一个或多个@Async-annotated方法(在这种情况下,Spring抽象任务执行程序并使您的操作变得简单)或明确使用任务执行程序。当您的代码执行时,它开始从池中获取可用的线程。如果您总是一次处理一个请求而没有立即的后续请求,那就没问题。 (在这种情况下,您可能正在尝试使用错误的技术来解决您的问题。)但是,如果它是一个Web应用程序,它暴露给任意(甚至已知)客户端可能正在通过请求锤击端点,您将快速耗尽线程池,请求将开始堆积,等待线程可用。仅仅因为这个原因,你应该意识到你可能走错了路 - 如果你正在考虑这样的设计。

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

答案 5 :(得分:1)

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

@Service
public class smg {
  ...
  @Async
  public getCounter() {...}
}

在上下文文件中使用<context:component-scan base-package="ch/test/mytest"><task:annotation-driven/>

请参阅本教程:http://spring.io/blog/2010/01/05/task-scheduling-simplifications-in-spring-3-0/

在Tomcat7上对我很有用,你不需要管理一个线程池。