Java読書会の前回の課題図書「Java並列処理プログラミング」において、ブロッキング・メソッド(Threadクラスのsleepも含む)を呼び出すときに要求されるチェック例外InterruptedExceptionの取り扱いについて述べられていた。
同書籍の著者によるIBM developerWorksの記事Javaの理論と実践: 割り込み例外の処理の方が少し詳しく書かれている。
対処方法
簡単に箇条書きにまとめると、
- メソッドのthrows節にInterruptedExceptionを記述し上位へ伝播させる
- 1.が難しいとき(例:Runnableを実装したクラスのrunメソッドの中)、InterrptedExceptionをキャッチしてインタラプテッド・ステータスを復元する
- Threadを継承しているrunメソッドの中などスレッドの最上位をコントロールしている場合で、InterruptedExceptionの発生でスレッドが終了する場合は、1.、2.の方法でなくても(例:キャッチして何もしない)よい
となります。
考察
例外を考えるには、「契約による設計」の事前条件・事後条件を整理するとすっきりします。
ケース1
Thread.sleep(n * 1000)を使用するメソッドm1があって、このm1の事後条件として「n秒間待ってXXXする」のように規定されているとします。
InterruptedExceptionの発生によりThread.sleepがn秒間より前に中断されたとします。このときメソッドm1が何事もなかったかのようにリターンするのは事後条件違反です。
したがって、1.のように上位へ例外InterruptedExceptionあるいはそれに代わる例外をスローするのが望ましいことになります。
void m1(final int n) throws InterruptedException { : 何らかの処理 Thread.sleep(n * 1000); : XXX処理 }
ケース2
Thread.sleep(n * 1000)を使用するメソッドm2があって、このm2の事後条件にはn秒待つのが必須ではないとします。たとえば、少し待ってからリトライする処理が例です。n秒待ちますが、それより早くリトライ処理をしても事後条件違反にはならない場合です。
この場合、InterruptedExceptionが発生してThread.sleepがn秒間より前に中断されたとしても、リトライ回数に達していなければリトライを続ける必要があります。
もしリトライ回数に達していないのに処理を抜けてしまうと、それが事後条件違反となります。
したがって3.のようにcatch節で何もせず繰り返しリトライ処理を続けるのが望ましいことになります。もし2.のようにcatch節でインタラプテッド・ステータスを復元してしまうと、次のThread.sleepでInterruptedExceptionが発生してしまいます。
void m2(final int n) { : 何らかの処理 while (retried < numRetry) { try { Thread.sleep(n * 1000); : XXX処理 break; } catch (InterruptedException e) { } retried++; } }