tomcat 6线程池用于异步处理

简短的问题: 在Tomcat 6应用程序中 – 如何运行(单独的)线程池?

运行线程池的最佳解决方案是什么?

长问题:
我这里有一个简单的需要;
轮询数据库中的某些数据,同时允许Web客户端等待答复(长轮询连接)。
当数据在数据库中可用时,我会向相关客户发送回复。

这么说,我不想深入研究任何框架( quartz scheduler可能?)。

因此,正如我总结的那样,我需要一个线程池来在后台完成工作。

因此,如果Thread是我即将使用的(实际上是Runnable ),哪个类可以组织它呢? 有没有ThreadPool解决方案? 有什么建议?

回答你的简短问题:

在JVM中,线程池在java.util.concurrent.ExecutorService接口后面抽象。 这个接口有不同的实现,但在大多数情况下,这个接口的方法就足够了。

要创建特定的线程池,请查看java.util.concurrent.Executors类: http : //docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html其中包含静态工厂用于创建ExecutorService接口的不同实现的方法。 您可能对newFixedThreadPool(int threadsNumber)newCachedThreadPool方法感兴趣。

有关JVM中Executors更多常规信息,您可能需要阅读以下Oracle教程: http : //docs.oracle.com/javase/tutorial/essential/concurrency/executors.html

因此,要在Tomcat下使用线程池( ExecutorService ),您应该执行以下操作:

0.1。 如果尚未完成,则在javax.servlet.ServletContextListener接口的web.xml实例中创建并注册(这将作为Web应用程序的入口点)。

0.2。 在contextInitialized(ServletContextEvent)方法中,您创建ExecutorService (线程池)的实例并将其存储在ServletContext属性映射中,以便可以从webapp中的任何位置访问它,例如:

 // following method is invoked one time, when you web application starts (is deployed) @Override public void contextInitialized(ServletContextEvent servletContextEvent) { // ... final int numberOfThreads = ...; final ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads); // starts thread pool final ServletContext servletContext = servletContextEvent.getServletContext(); servletContext.setAttribute("threadPoolAlias", threadPool); // ... } // following method is invoked one time when your web application stops (is undeployed) public void contextDestroyed(ServletContextEvent servletContextEvent) { // following code is just to free resources occupied by thread pool when web application is undeployed final ExecutorService threadPool = (ExecutorService) servletContextEvent.getServletContext().getAttribute("threadPoolAlias"); threadPool.shutdown(); } 

0.3。 在Servlet.service方法的某个地方或webapp中的任何地方(您应该能够从webapp获得对ServletContext引用):

 Callable callable = new Callable() { public ResultOfMyTask call() { // here goes your task code which is to be invoked by thread pool } }; final ServletContext servletContext = ...; final ExecutorService threadPool = (ExecutorService) servletContext.getAttribute("threadPoolAlias"); final Future myTask = threadPool.submit(callable);; 

您应该存储对myTask的引用,并可以从其他线程查询它以查明它是否已完成以及结果是什么。

希望这可以帮助…

对于您的用例,您可以利用java平台中提供的Timer和TimerTask定期执行后台任务。

 import java.util.Timer; import java.util.TimerTask; TimerTask dbTask = new TimerTask() { @Override public void run() { // Perform Db call and do some action } }; final long INTERVAL = 1000 * 60 * 5; // five minute interval // Run the task at fixed interval new Timer(true).scheduleAtFixedRate(dbTask, 0, INTERVAL); 

请注意,如果任务完成时间超过五分钟,则后续任务将不会并行执行。 相反,他们会在队列中等待,并在前一个完成后一个接一个地快速执行。

您可以将此代码包装在单个类中,并从启动servlet调用。

通常,在Servlet容器之外执行这些类型的定期后台作业是一种很好的做法,因为它们应该被有效地使用,以提供HTTP请求。

对于简单的后台任务,您根本不需要任何类型的线程池。 您需要做的就是:

  1. 启动一个post
  2. 检查后台进程是否应该停止
  3. 让它轮询数据库
  4. 将新近轮询的数据存储在可访问的地方
  5. 检查后台进程是否应该停止
  6. 去睡觉
  7. 重复步骤#2-#7

编写一个ServletContextListener ,它启动一个线程来执行您在实现Runnable的类中定义的上述步骤。 在contextDestroyed方法中,设置一个标志,触发上面#2和#5中指示的检查,然后调用Thread.interrupt以便线程终止。

您的后台任务绝对不应该尝试同步向客户端发送消息。 相反,在监视器上使用某些其他机制(如Object.notify通知等待轮询器(由于您不想阻止客户端检查当前状态,因此没有任何意义),更新某种时间戳,或者只是轮询客户端检查#4中可用的当前数据。