简短的问题: 在一个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
方法。
有关更多常规信息Executors
在JVM中你可能需要阅读以下Oracle的教程: http://docs.oracle.com/javase/tutorial/essential/concurrency/executors.html
因此,使用线程池( ExecutorService
)在Tomcat下,你应该做到以下几点:
0.1。 创建并注册在web.xml
的实例javax.servlet.ServletContextListener
接口(这会像一个入口点到你的web应用),如果它尚未完成。
0.2。 在contextInitialized(ServletContextEvent)
方法,您创建的实例ExecutorService
(线程池),并将其存储在ServletContext
属性图,以便它可以从任何一点在你的web应用例如访问:
// 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
方法或在你的web应用的任何地方(你应该能够获得参考ServletContext
几乎从任何地方的webapp):
Callable<ResultOfMyTask> callable = new Callable<ResultOfMyTask>() {
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<ResultOfMyTask> myTask = threadPool.submit(callable);;
你应该参考存储myTask,可以从其他线程调用它来发现它是否完成,结果是什么。
希望这可以帮助...
对于你的使用情况,您可以利用Timer和TimerTask在Java平台可用来定期执行后台任务。
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请求。
对于一个简单的背景任务,你不需要任何种类的线程池都没有。 所有你需要做的是:
- 启动一个线程
- 检查是否后台进程应停止
- 有它轮询数据库
- 存放新鲜调查数据访问的地方
- 检查是否后台进程应停止
- 去睡觉
- 重复步骤#2-#7
写ServletContextListener
即启动一个线程来执行,你在实现一个类定义上述步骤Runnable
。 在contextDestroyed
方法,设置触发#2和#5以上指出的检查的标志,然后调用Thread.interrupt
所以你的线程终止。
你的后台任务绝对不应该试图同步发送邮件给客户。 取而代之的是,通知等待使用一些其他的机制,像轮询Object.notify
显示器上(这并不真正使任何意义,因为你不希望阻止客户检查当前状态),更新一些类型的时间戳,或者只是有轮询的客户端检查#4提供的最新数据。