dポイントプレゼントキャンペーン実施中!

ServletContextLisntener#contextDestroyedついて
下記の様なコードがあったとします。
Tomcat上で動くServletContextListenerImpl#contextDestroyed内の処理にて
ポイント1とポイント2を確実に通過するには
どのような設定orコーディングが必要ですか?
※宿題ではありません。月曜に製造完です。(..);
class ThreadManager {
  private static final ThreadManager manager = new ThreadManager();
  private final Thread t;
  private boolean isStop;
  pulbic ThreadManger () {
    isStop = false;
    final Thread t = new Thread () {
      public void run () {
        for ( ; ! this.isStop ; ) {
          Thread.sleep(300000);
        }
      }
      //ポイント1
    }
    t.start();
    this.t = t;
  }
  public static ThreadManager getInstance () {
    return manager;
  }
  public void stop () {
    this.isStop = true;
  }
  public void isAlive () {
    return this.isAlive();
  }
}
public class ServletContextListenerImpl implements ServletContextListener {
  public void contextInitialzed () {}
  public void contextDestroyed () {
    final ThreadManager manager = ThreadManager.getInstance();
    manager.stop();
    while (manager.isAlive()) {
      //Thread.sleep(2000)する
    }
    //ポイント2
  }
}

A 回答 (2件)

> ServletContextListenerImpl#contextInitialzed


> がコールされ数十秒でスレッドが強制終了されて
> ServletContextListenerImpl#contextDestroyed
> のポイント2を通過できません

contextInitialzed は起動時、contextDestroyed は終了時に呼ばれるはずですが、
・Eclipse を使って Tomcat を起動した場合には両方とも正常に呼ばれる
・Tomcat 単独で動かした場合には終了時に contextDestroyed が呼ばれていない
という事ででしょうか?

これらをどうやって確認したのでしょう?
catalina.out などのへの出力を見たのでしょうか?

基本的にはTomcatの起動の仕方によって変わることは無いと思います。
ただし、JVMが強制終了させた場合などは contextDestroyed が呼ばれないことはありえます。例えば、linuxで kill -KILL によってTomcatの実行JVMを終了させた場合は呼ばれないと思われます。

> 基本的にinterruptするのではなく、
> 質問で言えばsleep中(処理中)とし
> 処理を終えたら次の処理はせず安全に停止する

interrupt を使ってもこのような動作はできます。
というか、このような動作をさせるのが interrupt の最も一般的な使い方だと思います。
sleep や wait の最中であれば、InterruptedException が発生しますが、それ以外であれば、Thread.isInterrupted() でtrueが帰るようになるだけです。

例) ------------------------------------------
public void run () {
 while (!Thread.currentThread().isInterrupted()) {
  // ・
  // ・
  // ・
  try {
   Thread.sleep(300000);
  } catch (InterruptedException e) {
   Thread.currentThread().interrupt();// 割り込みフラグを立て直す

   // sleep 中に interrupt された場合の処理
  }
 }
}
----------------------------------------------

http://www.ibm.com/developerworks/jp/java/librar …
    • good
    • 0

細かいスペルミスやセミコロンや例外処理の記述漏れなどを修正したとしても、このコードはコンパイルエラーになるのでは?


さらには根本的にスレッドセーフになっていません。


以下、スペルミスなど以外の問題点を書きます。

1. ポイント1の場所

 ポイント1はどのメソッドにも含まれていないので、どうやっても通過しません。
 強いて言えば、ポイント1にフィールド定義などを書いた場合はオブジェクトが生成されたタイミングで実行されることにはなります。


2. isStop

 無名クラス内で isStop を参照していますが、this は無名クラス自身のオブジェクトを指してしまいますので、単に isStop とするか ThreadManager.this.isStop としなければいけません。
 また、isStop を複数のスレッドから参照・変更しようとしているようですが、同期化もされおらず volatile でもないフィールドは正しい値が見えない可能性が有ります。

 キャンセルには interrupt を使うのが定石です。
http://www.itarchitect.jp/technology_and_program …

 interrupt を使うのが難しければ、Thread を無名クラスではなく名前を持った内部クラスとして定義し、その中に isStop を持たせて、同期化か volatile にすれば一応スレッドセーフにはなると思います。isStop が false になっても sleep が終わるまではチェックされませんし、お勧めはしませんが。


3. isAlive()

> public void isAlive() {
> return this.isAlive();
> }

void 型のメソッドで自分自身の呼出し結果を返そうとしても出来ないですよね。
また、

> while (manager.isAlive()) { ・・・

も void 型のメソッドではコンパイルエラーになりますよね。
ただし、戻り値を boolean に変えても、無限再帰に陥るだけですが・・・。

 下記のようなコードにするつもりで書き間違えたのかも知れませんが、これでもスレッドセーフの問題は残りますし、isStop が false になっていても、スレッドはまだ sleep 中かも知れません。

public boolean isAlive() {
 return !this.isStop; // ※ これもスレッドセーフではない!
}


4. ServletContextListenerImpl の各メソッドのシグネチャー

 ServletContextListenerImpl の各メソッドは ServletContextListener のメソッドを実装しようとしているのだと思いますが、引数が違います。
http://mergedoc.sourceforge.jp/tomcat-servletapi …

両方とも ServletContextEvent を引数に取らないと、オーバライドした事になりません。


以上の他にも、スペルミスなどはいろいろ有るようですので修正が必要です。


 ポイント2を通す方法、というよりこれらプログラムを動かす方法ですが、 web.xml でこのリスナーに登録すればよいかと。
http://www.techscore.com/tech/J2EE/Servlet/7.html

ただし、他のどこかで ThreadManager をロードするようにしていなければ、ThreadManager で定義したスレッドは、Webアプリケーションがシャットダウンするときに始まってすぐにストップされることになるでしょう。

また、isAlive の実装次第では manager の stop 待ちのループを while ではなく、for を使って回数制限したほうが良いかもしれません。

 for (int i = 0; i < 10 && manager.isAlive(); i++) {
  //Thread.sleep(2000)する
 }

このようなスレッドの終了待ち処理も、出来れば Thread.join(long) を使うような形に持っていった方が良いとは思いますが。


ちなみにJavaのスレッドプログラミングについては下記の書籍が参考になると思います。
http://www.amazon.co.jp/dp/4797337206

参考URL:http://www.ibm.com/developerworks/jp/java/librar …

この回答への補足

遅くなり申し訳ありません。
記述ミスや抜けなど読み取っていただき
しかも的確なアドバイスをいただきありがとうございます。

もう1点質問させてください。
(再度コードを載せると長くなってしまうので
このまま初回の質問のコードをベースに質問させていただきます。
すみません。)

ServletContextListenerImpl#contextInitialzed
(引数抜け)
がサーバー(tomcat)停止時にコールされ
managerが停止するまで待って
安全にスレッド停止したいというコードなのですが、
manager.stop後managerが終了するまで
数分かかる場合についての安全停止についてです。
(基本的にinterruptするのではなく、
質問で言えばsleep中(処理中)とし
処理を終えたら次の処理はせず安全に停止する)

eclipseから起動したtomcatを
eclipseから停止した場合
managerが停止するまで数分掛かろうが
ServletContextListenerImpl#contextDestroyed
のポイント2を通過できます。

しかし、
warでデプロイしたアプリを
サービス登録したtomcatで稼動させ
サービスを停止した場合
ServletContextListenerImpl#contextInitialzed
がコールされ数十秒でスレッドが強制終了されて
ServletContextListenerImpl#contextDestroyed
のポイント2を通過できません
この場合もポイント2を通過する方法?設定?はありませんか?

質問の仕方もへたくそで申し訳ありませんが、
宜しくお願いします。

補足日時:2010/10/30 13:46
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!