2011-07-29 12 views
2

JEE環境でのタスクスケジューリングのためにScheduledExecutorServiceを取得しました。これらのタスクの中には、中断されたときにリソースが開かれているものがあります(例えば、Luceneのようなサードパーティのライブラリを開いたファイル)。ScheduledExecutorService.shutdownNow()ScheduledExecutorServiceでスケジュールされたタスクインスタンスの取得

私はスレッドが自分自身の実行を止めることはできないことを知っています:スレッドを停止するために使用される方法は、割り込みフラグを頬を傾けてメソッドの実行を停止し、スレッドがブロック(例えばwait()、sleep )など)、または割り込み可能なチャネルでIO操作を行っている場合、Thread.interrupt()InterruptedExceptionを立ち上げます。どちらの場合も、finallyブロックを実行する必要があります。 参照:http://download.oracle.com/javase/1,5.0/docs/api/java/lang/Thread.html#interrupt%28%29。明らかに、私はすでにタスククラスのfinallyブロックでリソースを解放しようとしましたが、いくつかの環境(例えばCentOS)ではスレッドが中断したときfinallyブロックは実行されません。そして、私は公式のJavaドキュメントには、この非常にクールなノートを見つけた:

注:試してみるか、キャッチコードが実行されている間、JVMが終了した場合、 finallyブロックが実行されないことがあります。同様に、tryまたはcatchコードを実行しているスレッド が中断または終了した場合、アプリケーション全体が として継続しても、最後に ブロックが実行されないことがあります。

私が必要とするのは、リソースの解放を強制するタスククラスにいくつかのパブリックメソッドを実装するためにスケジュールされたすべてのタスクへの参照です。 ScheduledExecutorServiceからタスククラスへの参照を取得できますか?または、私の問題をより良い方法で解決するための素晴らしいアイデアがありますか?

最初の解決策:それをラップしてください!

ScheduledExecutorServiceのラッパークラスを作成し、このようなプロパティを追加します。それに

private IdentityHashMap<ScheduledFuture<?>, Runnable> taskList; 

を私たちが直接任意のRunnableオブジェクトにアクセスする、またはそれに関連するScheduledFutureですることができます。ラッパーのインスタンス化のために、私はExecutors.newScheduledThreadPool()メソッドからScheduledExecutorServiceを取得し、それを私のラッパーに渡すことができます。

もう1つ解決策:それを拡張してください!

ScheduledThreadPoolExecutorを拡張し、IdentityHashMapプロパティを追加して、ジョブのスケジュールまたはキャンセル方法をすべて上書きして、マップから参照を追加または削除します。

両方のソリューションに問題がありますか?

ラッパーまたは拡張クラスの呼び出し元がSchedulerFuture<?>オブジェクトを受け取った場合は、「カプセル」をバイパスしてSchedulerFuture<?>.cancel()メソッドでジョブをキャンセルすることができます。ラッパーを使用すると、呼び出し側にSchedulerFuture<?>参照を渡すことは避けられますが、拡張クラスを使用することはできません(拡張クラスで独自のメソッドを作成すると、ラッパーと同じ結果が得られますが、非常に混乱します)。

エレガントなソリューション:独自のスケジューラ!カジュに感謝しています...

  1. 実際 スレッドをキャンセルするだけでなくへRunnableオブジェクトを操作するのカスタムcancel()方法を実装 ScheduledFutureインタフェース
  2. の一の実装でRunnableを飾る decorateTask()方法
  3. を上書きするScheduledThreadPoolExecutorを拡張リソース解放のために を強制してください。

詳細とコードの例については、postをご覧ください。

+0

VMが終了していると思われますか? finallyブロックを実行していませんか?その場合は、すべてのタスクがキャンセルされたとき(つまり、エグゼキュータ上で終了を待って実装する)にVMを終了させるのはなぜですか?それはちょうど競争状態になる可能性があります..? – Toby

+0

これは、Tomcatアプリケーションがシャットダウンされ、Luceneライブラリがサーバ内の共有リソースである場合に発生します。したがって、アプリケーションのすべてのスレッドはキャンセルされますが、VMはまだ生きており、ライブラリは他のWebアプリケーションに引き続き使用できます。一方、アプリケーションをシャットダウンするときは、 'awaitTermination(数分)'の後に 'shutdown()'、次に 'shutdownNow()'を実行します。問題:Luceneインデックスの再構築は非常に長いタスク(数時間)です。したがって、 'shutdownNow()'が呼び出されたときにインデックス再構築が実行されている場合、スレッドは中断されますが、リソースは解放されません。 – ggarciao

+0

hmmm、私はluceneのバグとしてそれを上げたい!スレッドはIMOを中断するために適切に応答する必要があります(正しい中断ポリシーを実装する必要があります)。あなたはこれを回避するために非常に努力する必要があります。 – Toby

答えて

0

あなたは何を予定していますか?タスクはどのように見えますか?私はfinallyブロックが実行されないと信じることは非常に難しいと感じています。私はあなたがスケジューリングしたタスクだと推測しますが、リソースが漏れているため実行し始めていません(最終ブロックは実行されないため)

CentOS上のVM実装が本当に悪いようです実際には最終的にブロックを実行していません。他のVMの実装でそれについて聞いたことはありません。

スケジューリングされたすべてのタスクを参照するのではなく、ScheduledThreadPoolExecutorをサブクラス化してdecorateTaskメソッドをオーバーライドして、クラスでタスクをデコレートし、キャンセル呼び出しをインターセプトすることができます。

+0

私の定期的なスケジュールされたタスクはLuceneインデックスを更新します(http://lucene.apache.org/java/docs/index.html)。 finally私たちの場合、finallyブロックの問題は信じられないほどですが、私たちの診断はかなり良いです。私たちのCentOSではfinallyブロックに決して到達しません(スレッド中断の後)。 プールエグゼキュータの拡張に関するアドバイスをありがとうございます。私はそれを行う方法を確認します。 – ggarciao

+0

@ggarciao - あなたはfinallyブロック内で中断が起こっていると確信していますか? Thread.stop()のようなことはしていませんか?通常のスレッドの中断は常にfinallyブロックをヒットするべきです。また、スレッドの中断に敏感で、最終的にブロックの残りの部分をスキップするfinallyブロックの中で何かをやっている可能性はありますか? – jtahlborn

+0

本当に、私はLuceneの読者、作家、ディレクトリを閉じていますが、その操作は他のtry-catchブロックの中にあります。 – ggarciao

関連する問題