我有一个部署到Tomcat服务器的WAR文件,其中一个类将在启动时被调用,然后init()方法将安排一个计时器每5小时触发一次以执行某些任务。
我的init()代码如下所示:
public void init()
{
TimerTask parserTimerTask = new TimerTask() {
@Override
public void run() {
XmlParser.parsePage();
}
};
Timer parserTimer = new Timer();
parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD);
}
我的应用程序运行没有问题,但是当我使用 /etc/init.d/tomcat7 stop 关闭Tomcat时,我检查日志(catalina.out)它有一个这样的条目:
严重:Web应用程序[/ MyApplication]似乎已启动一个名为[Timer-0]的线程但未能将其停止。这很可能会造成内存泄漏。
我知道这是由我安排计时器引起的,但我的问题是:
setDeamon
设置为true,所以定时器是否应该阻止Tomcat关闭而不是继续运行?谢谢!
更新
我根据一些搜索和DaveHowes的答案将我的代码更改为以下内容。
Timer parserTimer;
TimerTask parserTimerTask;
public void init()
{
parserTimerTask = new TimerTask() {
@Override
public void run() {
XmlParser.parsePage();
}
};
parserTimer = new Timer();
parserTimer.scheduleAtFixedRate(parserTimerTask, 0, PERIOD);
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
Logger logger = Logger.getRootLogger();
logger.info("DETECT TOMCAT SERVER IS GOING TO SHUT DOWN");
logger.info("CANCEL TIMER TASK AND TIMER");
otsParserTimerTask.cancel();
otsParserTimer.cancel();
logger.info("CANCELING COMPLETE");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
}
现在我的新问题:
谢谢!
更新
它不起作用。我在contextDestroyed()方法中放了一些日志语句,在关闭Tomcat之后,日志文件只有以下内容:
PowderGodAppWebService - > [07 Feb 2012 04:09:46 PM] INFO(PowderGodAppWebService.java:45)::检测TOMCAT服务器即将关机 PowderGodAppWebService - > [07 Feb 2012 04:09:46 PM] INFO(PowderGodAppWebService.java:46)::取消定时器任务和定时器
取消完成不存在。
我还检查了正在运行的进程(我不是Linux专家,所以我只使用Mac的Activity Monitor。
固定
我将代码更改为parserTimer = new Timer(true);
,以便我的计时器作为守护程序线程运行,因为在Tomcat实际关闭后调用contextDestroyed()
。
“在向任何ServletContextListener通知上下文破坏之前,所有servlet和过滤器都将被销毁。”
http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html
答案 0 :(得分:59)
不在Java EE环境中使用Timer
!如果任务抛出运行时异常,那么整个Timer
将被终止并且不再运行。您基本上需要重新启动整个服务器以使其再次运行。此外,它对系统时钟的变化很敏感。
请改用ScheduledExecutorService
。它对任务中抛出的异常和系统时钟的变化都不敏感。您可以通过shutdownNow()
方法关闭它。
以下是整个ServletContextListener
实现的外观示例(注意:由于新的web.xml
注释,@WebListener
无需注册):
@WebListener
public class BackgroundJobManager implements ServletContextListener {
private ScheduledExecutorService scheduler;
@Override
public void contextInitialized(ServletContextEvent event) {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new YourParsingJob(), 0, 5, TimeUnit.HOUR);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
}
}
答案 1 :(得分:1)
servlet destroy方法被调用,因为servlet即将被卸载。您可以从那里取消计时器,前提是您更改了parserTimer本身的范围以使其成为实例变量。我没有看到问题,前提是你只能从init和destroy中访问它。
servlet引擎可以在它认为合适的时候自由卸载servlet,但是在实践中我只看到它被称为servlet引擎被停止 - 其他人可能有其他经验,虽然这可能证明我错了。
答案 2 :(得分:-1)
尝试使用框架进行调度。
如果您调整Spring Framework,则可以使用内置调度功能。
使用Spring进行调度时,我从来没有停止应用服务器的任何问题。
答案 3 :(得分:-2)
将 parseTimer.purge()放入onContetexyDestroyed.It将删除队列中的所有计时器(如果存在)。