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

2022-09-02 00:11:33

我有一个WAR文件部署到Tomcat服务器,其中一个类将在启动时被调用,然后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. 我没有设置为 true,所以计时器不应该阻止 Tomcat 关闭,而不是继续运行吗?setDeamon
  2. 在我的应用程序中,我可以检测到Tomcat将要关闭并取消我的计时器吗?
  3. 我可以使用哪些其他解决方案来解决此问题?

谢谢!

更新

我根据一些搜索和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. 我先取消计时器任务,然后取消计时器,这是正确的吗?
  2. 我还有其他应该做的事情吗?

谢谢!

更新

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

PowderGodAppWebService -> [07 Feb 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:45):: DETECT TOMCAT SERVER IS WILL OFF OFF POWDERGodAppWebService -> [07 Feb 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:46):: CANCEL TIMER TASK AND TIMER

取消完成不存在。

我还检查了正在运行的进程(我不是Linux专家,所以我只使用Mac的活动监视器。

  • 确保没有 Java 进程正在运行
  • 启动Tomcat,注意该java进程的PID
  • 停止雄猫
  • 发现雄猫进程不见了
  • 启动Tomcat,注意该java进程的PID
  • 部署我的战争文件
  • 对流程进行采样,请参阅 [Timer-0] 线程是否存在
  • 关闭雄猫
  • 发现流程仍然存在
  • 对流程进行采样
  • 查看 [Timer-0] 是否仍然存在

固定

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

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

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


答案 1

不要在 Java EE 环境中使用!如果任务引发运行时异常,则整个任务将被终止,并且不会再运行。您基本上需要重新启动整个服务器才能使其再次运行。此外,它对系统时钟的变化很敏感。TimerTimer

请改用 ScheduledExecutorService。它对任务中引发的异常或系统时钟的更改不敏感。您可以通过其方法将其关闭。shutdownNow()

下面是整个实现如何显示的示例(注意:由于新的注释,不需要注册):ServletContextListenerweb.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();
    }

}

答案 2

servlet 销毁方法在 servlet 即将被卸载时调用。您可以从内部取消计时器,前提是您更改了解析器计时器本身的范围以使其成为实例变量。我不认为这有什么问题,只要你只能从init内部访问它并销毁。

servlet引擎可以自由地卸载servlet,只要它认为合适,但在实践中,我只在servlet引擎停止时才看到它 - 其他人可能有其他经验,尽管这可能证明我错了。


推荐