
Two-Phase Terminationパターン
ポイントは、
- 安全に終了すること(安全性)
- 必ず終了処理を行うこと(生存性)
- 終了処理を出したらできるだけ早く終了処理に入ること(応答性)
約500ミリ秒間隔でカウントアップを行っているスレッドを、約10秒後に終了させるというプログラムを考える。
(コード全体は本書を参照のこと)
public class Main {
public static void main(String[] args) {
try {
CountupThread t = new CountupThread();
t.start();
Thread.sleep(10000);
t.shutdownRequest();
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class CountupThread extends Thread {
private long counter = 0;
private volatile boolean shutdownRequested = false;
public void shutdownRequest() {
shutdownRequested = true;
interrupt();
}
...
public final void run() {
try {
while (!isShutdownRequested()) {
doWork();
}
} catch (InterruptedException e) {
} finally {
doShutdown();
}
}
private void doWork() throws InterruptedException {
counter++;
System.out.println("doWork: counter = " + counter);
Thread.sleep(500);
}
...
}
登場人物
TerminationRequester役
TerminationRequester役は、Terminator役へ終了要求を出す。サンプルプログラムでは、Mainクラスがこの役をつとめた。
Terminator役
Terminator役は、終了要求を受けて、実際に終了処理を行う。Terminator役は、終了要求を表すshutdownRequestメソッドを提供する。Terminator役は、自分が終了要求を受けたかどうかを表すフラグ(ラッチ)を持っている。サンプルプログラムでは、CountupThreadクラスがこの役をつとめた。
考えるを広げるヒント
Threadクラスのstopメソッドを使ってはいけない
stopを使うと、クリティカルセクションの実行途中であっても、スレッドはjava.lang.ThreadDeathという例外を投げて終了する。
フラグのテストだけでは不十分
shutdownRequestメソッドの中で、interruptメソッドを呼ぶ必要があるか。shutdownRequestフラグだけではだめか。shutdownRequestedフラグだけだと、例えばスレッドがsleepしていたら、いくらフラグをtrueにしても終了処理を始めてくれない。sleep時間が過ぎれば終了処理を始めてくれるかもしれないが、それでは応答性が悪くなってしまう。interruptメソッドを使えば、sleepやwaitを中断することができる。
インタラプト状態のテストだけでも不十分
今度は逆に、interruptメソッドを呼ぶだけではだめか。例えば、スレッドが実行しているdoWorkメソッドの中で次のように記述してしまうとうまくいかない。
private void doWork() throws InterruptedException {
counter++;
System.out.println("doWork: counter = " + counter);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
interruptメソッドについて
interruptメソッドを呼び出すと、対象のスレッドは常にInterruptedExceptionを投げると思っているかもしれないが、それは誤解である。interruptというメソッドは、スレッドのインタラプト状態を変えるだけである。インタラプト状態とは、スレッドがインタラプトされているかどうかを表している状態のこと。sleep, wait, joinがInterruptedExcpetionを投げるのは、それらがメソッドの内部でスレッドのインタラプト状態を調べ、明示的にInterruptedExceptionを投げているからである。wait, sleep, joinでいったんInterruptedExceptionが投げられると、スレッドはインタラプト状態ではなくなる。
関連
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その1)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その2)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その3)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その4)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その5)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その6)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その7)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その8)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その9)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その10)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その11)