关闭tomcat时停止计划的计时器

时间:2012-02-07 08:32:52

标签: java java-ee tomcat tomcat7

我有一个部署到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]的线程但未能将其停止。这很可能会造成内存泄漏。

我知道这是由我安排计时器引起的,但我的问题是:

  1. 我没有将setDeamon设置为true,所以定时器是否应该阻止Tomcat关闭而不是继续运行?
  2. 我可以在我的应用程序中检测Tomcat是否会关闭并取消我的计时器?
  3. 我可以用什么其他解决方案来解决这个问题?
  4. 谢谢!

    更新

    我根据一些搜索和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) {
    
    }
    

    现在我的新问题:

    1. 我首先取消TimerTask然后取消Timer,这是正确的吗?
    2. 我还有其他事吗?
    3. 谢谢!

      更新

      它不起作用。我在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。

      • 确保没有正在运行的java进程
      • 启动Tomcat,记下该java进程的PID
      • 停止Tomcat
      • 发现Tomcat进程已消失
      • 启动Tomcat,记下该java进程的PID
      • 部署我的战争文件
      • 对过程进行采样,参见[Timer-0]线程
      • 关闭Tomcat
      • 发现该过程仍然存在
      • 对流程进行抽样
      • 请参阅[Timer-0]仍然存在

      固定

      我将代码更改为parserTimer = new Timer(true);,以便我的计时器作为守护程序线程运行,因为在Tomcat实际关闭后调用contextDestroyed()

      “在向任何ServletContextListener通知上下文破坏之前,所有servlet和过滤器都将被销毁。”

      http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html

4 个答案:

答案 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将删除队列中的所有计时器(如果存在)。