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

Der*_* Li 22 java tomcat java-ee 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);
}
Run Code Online (Sandbox Code Playgroud)

我的应用程序运行没有问题,但当我使用/etc/init.d/tomcat7停止关闭Tomcat时,我检查日志(catalina.out)它有一个这样的条目:

严重:Web应用程序[/ MyApplication]似乎已经启动了一个名为[Timer-0]的线程,但未能阻止它.这很可能造成内存泄漏.

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

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

谢谢!

UPDATE

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

}
Run Code Online (Sandbox Code Playgroud)

现在我的新问题:

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

谢谢!

UPDATE

它不起作用.我在contextDestroyed()方法中放了一些日志语句,在关闭Tomcat之后,日志文件只有以下内容:

PowderGodAppWebService - > [2012年2月7日04:09:46] INFO(PowderGodAppWebService.java:45):: DETECT TOMCAT服务器即将关闭PowderGodAppWebService - > [2012年2月7日04:09:46] INFO(PowderGodAppWebService. java:46)::取消定时器任务和定时器

CANCELLING COMPLETE不在那里.

我还检查了正在运行的进程(我不是Linux专家,所以我只使用Mac的Activity Monitor.

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

固定

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

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

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

Bal*_*usC 63

千万不能使用Timer在Java EE的环境!如果任务抛出运行时异常,则整个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();
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 哇!这个答案非常有用*.非常感谢你.对于其他读者,一点澄清......`new YourParsingJob()`应该是接口的一个实例[Runnable](http://docs.oracle.com/javase/7/docs/api/java/lang/Runnable .html)正如JavaDoc所解释的那样. (2认同)
  • @ rekiem87:如果您手头有EJB,请使用`@ Schedule`代替.示例:http://stackoverflow.com/a/8482933和http://stackoverflow.com/q/7499769(底部).目前的问题是关于没有JPA和EJB的Tomcat.找到答案要小心...... (2认同)