Подтвердить что ты не робот

Остановить запланированный таймер при выключении tomcat

У меня есть файл 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);
}

Мое приложение работает без проблем, но когда я завершаю Tomcat с помощью /etc/init.d/tomcat7 stop, тогда я проверяю журнал (catalina.out), у него есть запись вроде этого:

SEVERE: веб-приложение [/MyApplication], похоже, запустило поток с именем [Timer-0], но не остановило его. Вероятно, это приведет к утечке памяти.

Я понимаю, что это вызвано тем, что я расписал таймер, но мой вопрос:

  • Я не установил setDeamon в значение true, поэтому не следует ли отключать таймер Tomcat, а не работать?
  • Могу ли я в своем приложении обнаружить, что Tomcat будет отключен и отменит мой таймер?
  • Каковы другие решения, которые я могу использовать для решения этой проблемы?

Спасибо!

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) {

}

Теперь мой новый вопрос:

  • Я отменяю TimerTask сначала, а затем Timer, это правильно?
  • Есть ли еще что я должен делать?

Спасибо!

UPDATE

Это не работает. Я положил некоторый оператор регистрации в метод contextDestroyed(), после того, как я завершаю Tomcat, файл журнала имеет только следующее:

PowderGodAppWebService → [07 Фев 2012 04:09:46 PM] INFO (PowderGodAppWebService.java:45):: DETECT TOMCAT SERVER НАХОДИТСЯ В ОТНОШЕНИИ PowderGodAppWebService → [07.02.2012 04:09:46] INFO (PowderGodAppWebService.java:46):: ОТМЕНА ТАЙМЕРА ТАЙНА И ТАЙМЕР

ОТМЕНА ПОЛНОГО не существует.

Я также проверил процессы, которые выполняются (я не эксперт по Linux, поэтому просто использую монитор активности Mac.

  • Убедитесь, что java-процесс не запущен.
  • Запустите Tomcat, обратите внимание на PID этого java-процесса.
  • Остановить Tomcat
  • Найден процесс Tomcat не работает
  • Запустите Tomcat, обратите внимание на PID этого java-процесса.
  • Разверните мой файл войны
  • Пример процесса, см. [Timer-0] thread есть
  • Shutdown Tomcat
  • Установлено, что процесс все еще существует
  • Пример процесса
  • См. [Timer-0] все еще существует

Fixed

Я изменил свой код на parserTimer = new Timer(true);, чтобы мой таймер работал как поток демона, потому что вызов contextDestroyed() вызывается после того, как Tomcat фактически завершает работу.

"Все сервлеты и фильтры будут уничтожены до того, как будут обнаружены сведения об уничтожении контекста ServletContextListeners.

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

4b9b3361

Ответ 1

Do не использовать 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();
    }

}

Ответ 2

Метод уничтожения сервлетов вызывается, когда сервлет вот-вот будет выгружен. Вы можете отменить таймер изнутри, указав, что вы изменили область действия самого parserTimer, чтобы сделать его переменной экземпляра. Я не вижу проблемы с этим при условии, что вы обращаетесь к нему только изнутри init и destroy.

Механизм сервлетов может разгружать сервлет всякий раз, когда он сочтет нужным, но в пратисе я только когда-либо видел, что он вызван, когда движок сервлета остановлен - у других людей может быть другой опыт, хотя это может оказаться мне неправильным.

Ответ 3

Попробуйте использовать фреймворк для планирования.

Если вы адаптируете Spring Framework, вы можете использовать возможности сборки в планировании.

При планировании с помощью Spring у меня никогда не было проблем с остановкой сервера приложений.

Ответ 4

Поместите parseTimer.purge() в свой onContetexyDestroyed.It удалит все таймеры в очереди, если они существуют.