Programming SHOT BARへようこそ。
ここのところマルチスレッド関連の話題で盛り上がっています。
今日はかつのりさまからのリクエストでスレッドのキャンセルにまつわる話です。
参考資料
以下の書籍を参考にしています。手元にある方は参照してみてください。
Thread#stop()とその問題点
Thread#stop()、Thread#suspend()、Thread#resume()という一連のスレッドの停止・再開メソッドは
非推奨メソッドなっており、使用してはいけません。
この件については、sunからわざわざ詳細な説明がアナウンスされています。
http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/misc/threadPrimitiveDeprecation.html
スレッドは外部から強制的に、しかし、安全に停止させることはできません。
なんらかの処理をしている最中に安全に中断するということは本質的に難しいのです。
その状況によって後始末を必要とします。
停止の要求に対して、「ちょっとまって。今中断するから」といった協力なしには安全な停止はなしえません。
フラグを立ててスレッドを停止させる
スレッドに対し、割り込みフラグを用意することで自発的な停止をさせることが出来ます。
/** 割り込みフラグ */
private volatile boolean stopFlag;
@Override
public void run() {
while(!this.stopFlag) {
// 実処理
}
}
上記サンプルではvolatileによってメモリ同期化されたフラグを利用して、
whileループを抜けるようにしてあります。
このように、外部からフラグを変更してやることでスレッドを自発的に停止させています。
しかし、Object#wait()やObject#join()などのスレッドをブロックするメソッドを
利用している場合、フラグを判定する箇所(キャンセルポイントといいます)まで
永遠にたどり着くことはないかもしれません。
個々のプログラマが独自に定義したスレッドの割り込みフラグの限界はここにあります。
APIのような不特定多数に利用してもらう場合には、やはり標準的な仕組みが欲しいですね。
Thread#interrupt()はその標準的なスレッドの割り込みフラグなのです。
Thread#interrupt()
Thread#interrupt()はThread標準で提供される停止フラグの通知機構です。
この呼び出しで対象のスレッドに対して割り込みフラグが立ちます。
この割り込みフラグはThread#isInterrupt()によって参照することができます。
staticなメソッドThread#interrupted()を使うと、現在のスレッドの割り込みフラグを
参照した上でフラグをリセットすることが出来ます。詳しくはjavadocを参照してください。
Object#wait()はThread#interrupt()によってスレッドの停止フラグが立つと、
waitすることを止めて、InterruptedExceptionをthrowします。
waitに限らず、ブロックされるメソッドは、基本的にスレッド停止フラグが立つと
InterruptedExceptionをthrowする作りになっています。
また、先のサンプルのように、ブロックされないスレッドにおいては、Thread#interrupt()を
利用しなくてもスレッドの停止を行うことが出来ます。
では、我々プログラマが独自に作成したスレッドではこの
どのような対処をする必要があるのでしょうか?
- InterruptedExceptionをthrowするように作る
- Thread#interrupt()で立てられた割り込みフラグを維持し、どこかのキャンセルポイントまで流す
- 自力でキャンセル処理をし、スレッドを終了させる
という3つの選択肢があります。
スレッドから呼び出される想定のメソッドで、処理が長引くことが想定される場合は
InterruptedExceptionをthrowする作りにしておくとよいでしょう。
例えば集計処理などの時間が掛かる処理ですね。
そもそも、そんなに時間の掛からない処理であれば、キャンセルポイントを作る必要もありません。
フラグに触らずそのまま処理を終えてどこかのキャンセルポイントにお任せします。
Thread#run()のような場所ではExceptionをthrowsするように宣言できませんから、
こういう箇所では自力でキャンセルポイントを実装してやります。
あらかじめThread#interrupt()による割り込みをチェックして
中断させるキャンセルポイントを作っておくことで、無駄な処理を続けることなく
速やかに停止させることが出来ます。
せっかちな現代人には必須の機能かもしれませんね。
業務での応用
集計処理など時間が掛かることが想定されるメソッドは、Thread#interrupt()による
停止をサポートしておくと、途中キャンセルされた場合に処理を止めることで
CPUリソースを無駄に食わずに済みます。
java.nio.channels.InterruptibleChannelによる通信であれば、
通信に割り込んで停止させることも可能です。
このあたり、業務要件で顧客から直接要望されることは少ないでしょうが、
パフォーマンスにこだわりがあるお客さん相手なら、
設計段階から中断についても考慮しておくと良いかもしれません。
投稿日時 : 2007年8月28日 20:28