前回は ExecutorService を使ったタスクの依頼・実行方法を見ました。 今回は ExecutorService のシャットダウン・終了方法を見ていきます。 ExecutorService を使うプログラムでは必ずシャットダウンを行う必要があるので忘れずに。
記事内容
- ExecutorService のシャットダウン方法
- ExecutorService のライフサイクル
- ExecutorService インターフェース(ライフサイクル関連)
- ExecutorService の終了メソッド
ExecutorService のシャットダウン方法
ExecutorService には2通りのシャットダウン方法がサポートされています:
- おだやかなシャットダウン・・・ shutdown() メソッド
- 唐突なシャットダウン・・・ shutdownNow() メソッド
それぞれのシャットダウン方法で ExecutorService が保持しているタスクがどのように扱われるかは後で見ていきます。 どちらの方法でも実行中のタスクは強制的に停止されるのではなく、タスク処理が終了するまで待つか、インターラプトによってタスクがキャンセルされるように促されるかのどちらかです。 ExecutorService は必ずシャットダウンする必要があるため、最低でも
ExecutorService exe = ...; try{ // exe にタスク実行の依頼 }finally{ exe.shutdown(); }
のように、確実にシャットダウンが行われるようにしなければなりません。
ExecutorService のライフサイクル
ExecutorService はシャットダウン用のメソッド shutdown(), shutdownNow() を呼ばれても、すぐにタスク処理が終わるわけではありません。 これらのメソッドから制御が返されても、ExecutorService が保持しているタスク(依頼された、もしくは実行中のタスク)は何らかの処理をしている可能性があります。 これを踏まえて、ExecutorService には以下の3つの暗黙に定義されているライフサイクルがあります:
- 実行中
- シャットダウン中・・・シャットダウン・メソッドが呼ばれたが、保持しているタスク処理が何らかの処理を行っている
- 終了・・・保持していたタスクの処理も完全に完了している
これはスレッドプログラミングのデザインパターン、Two-Phase Termination パターンですね。 shutdown(), shutdownNow() メソッドで、シャットダウン中に行われている処理は異なりますが、それは後ほど。
ExecutorService インターフェース(ライフサイクル関連)
ExecutorService に定義されているライフサイクル関連のメソッドには以下のようなものがあります:
package java.util.concurrent; public interface ExecutorService extends Executor{ void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit)throws InterruptedException; }
isTerminated(), isShutdown() メソッドは ExecutorService のライフサイクルにおける段階を返すメソッドです。 返される boolean 値によって以下のように段階が分かります:
isTerminated() → isShutdown() ↓ |
true | false |
true | 終了 | シャットダウン中 |
false | - | 実行中 |
awaitTermination() メソッドは、シャットダウン処理が終わって ExecutorService が終了段階になるまで待つメソッドです。
ExecutorService のシャットダウン・メソッド
では ExecutorService のシャットダウン・メソッド shutdown(), shutdownNow() がどのようにタスクを扱うかを見ていきましょう。 2つのシャットダウン・メソッドは、同じライフサイクルの段階でも行っている処理が異なっているので注意。
shutdown() メソッド
shutdown() メソッドはゆるやかに行うシャットダウンです。 基本的にはタスクが処理を終わるまで待つだけです。 shutdown() メソッドが呼ばれると
- 生成段階のタスクは、依頼することはできません。 依頼しようとすると ExecutorService に登録されている RejectedExceptionHandler が処理を行います。 デフォルトでは、黙ってタスクを捨てるか RejectedExecutionException を投げるかだそうです(試してねぇー)。
- 依頼段階のタスクは、いずれ実行に移されて処理が完了します。 処理中に例外が発生したりした場合は、依頼時に返された Future オブジェクトなどから情報を取得できます。
- 実行段階のタスクは、そのまま実行が継続されます。
shutdownNow() メソッド
shutdownNow() メソッドは唐突に行うシャットダウンです。 タスクは実行されないか、キャンセルされます。 ただし、タスク処理の強制的な停止はしません。 shutdownNow() メソッドが呼ばれると
- 生成段階のタスクは、shutdown() メソッドの場合と同様に依頼することはできません。
- 依頼段階のタスクは、実行段階に移されることなく、shutdownNow() メソッドの呼び出し側に返されます。
- 実行段階のタスクは、処理がキャンセルされます。
実行されていないタスクは shutdownNow() メソッドの返り値として返されますが、キャンセルされたタスクは ExecutorService 自体からは分かりません。 必要なら、依頼時に返された Future オブジェクトから辿って情報を取得してください。
Java並行処理プログラミング ―その「基盤」と「最新API」を究める―
- 作者: Brian Goetz,Joshua Bloch,Doug Lea
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/11/22
- メディア: 単行本
- 購入: 30人 クリック: 442回
- この商品を含むブログ (169件) を見る
- 作者: 荒木飛呂彦
- 出版社/メーカー: 集英社
- 発売日: 2011/09/16
- メディア: 文庫
- 購入: 11人 クリック: 98回
- この商品を含むブログ (29件) を見る