TomcatサーバーにWARファイルをデプロイしました。クラスの1つが起動時に呼び出され、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-> [2012年2月7日04:09:46 PM] INFO(PowderGodAppWebService.Java:45):: Tomcatサーバーを検出IS GOING TO SHUT DOWNシャットダウンPowderGodAppWebService-> [2012年2月7日04: 09:46 PM] INFO(PowderGodAppWebService.Java:46)::タイマータスクとタイマーのキャンセル
CANCELING COMPLETEはありません。
実行中のプロセスも確認しました(私はLinuxの専門家ではないので、Macのアクティビティモニターを使用しています)。
[〜#〜]固定[〜#〜]
Tomcatが実際にシャットダウンした後にparserTimer = new Timer(true);
が呼び出されるため、タイマーがデーモンスレッドとして実行されるように、コードをcontextDestroyed()
に変更しました。
「すべてのサーブレットとフィルタは、ServletContextListenersにコンテキストの破棄が通知される前に破棄されます。」
http://docs.Oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html
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();
}
}
サーブレットがアンロードされる直前に、サーブレットのdestroyメソッドが呼び出されます。タイマーをインスタンス変数にするためにparserTimer自体のスコープを変更した場合、そこからタイマーをキャンセルできます。 initとdestroy内からのみアクセスできれば、問題は発生しません。
サーブレットエンジンは、サーブレットが適切であると判断した場合はいつでも自由にアンロードできますが、実際には、サーブレットエンジンが停止したときに呼び出されることはありません。