第五章 (3): スレッドの割り込み
スレッドの割り込みについて。BlockingQueueのputやtakeは、InterruptedExceptionをスローする。例えばtakeの場合は、次の要素が無くてスレッドが待機状態になっている間、別スレッドが割り込みを行うとスローされる。割り込みされたスレッドは、出来るだけ早くブロックを抜ける努力をする。努力をするという点がポイントで、InterruptedExceptionは何かを強制するものではない。別スレッドがinterruptしたことを、そのスレッドに通知するのが目的である。スレッドAがスレッドBにinterruptするということは、「あなたの都合が良いときに今やっている処理を終えて下さい」というリクエストの意思表示である。BlockingQueueのtakeの場合は、待機状態にあるスレッドがInterruptedExceptionをスローすることにより、take処理を強制終了させるのか、それともまだ待機するのかをInterruptedExceptionをキャチする側で記述することになる。
通常、InterruptedExceptionが発生した場合の手順は以下の2つ。
- catchせずにそのままthrowする、もしくは終了処理を行った後に再throwすることにより、クライアント側に処理を委譲する
- currentThreadにinterruptし、処理を継続する
1.の手順ではInterruptedExceptionはクライアントにそのまま投げ直されるので、クライアント側で任意の処理を行うことになる。この場合は割り込まれるメソッドは"throws InterruptedException"と定義されるため、クライアント側で処理を記述することが強制される。2.の手順では自身に再割り込みすることで、処理を継続することになる。
簡単に確認するためのコードを書いてみる。次の要素が無いためブロックされるQueueを使ったConsumerスレッドを作る。1つ目のConsumerThreadは、割り込みされたらその旨を出力し、処理を中断させる。2つ目のInterruptConsumerThreadは、割り込みされたらその旨を出力し、更に自分でスレッドを取り戻し、メッセージ(interrupt!)を出力した後、終了する。
public class Sample { private static final int MAX_CAPACITY = 100; public static void main(String[] args) { BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(MAX_CAPACITY); Thread ct = new Thread(new Consumer(queue)); System.out.println("----- check by ConsumerThread -----------------------"); check(ct); System.out.println(); ct = new Thread(new InterruptConsumer(queue)); System.out.println("----- check by InterruptConsumerThread -----------------------"); check(ct); System.out.println(); } private static void check(Thread ct) { ct.start(); System.out.println("# no interrupt ..."); System.out.println("Thread#interrupted: " + Thread.interrupted()); System.out.println("CurrentThread#interrupted: " + Thread.currentThread().isInterrupted()); System.out.println("ConsumerThread#interrupted: " + ct.isInterrupted()); System.out.println(); System.out.println("# interrupt currentThraed ..."); Thread.currentThread().interrupt(); System.out.println("Thread#interrupted: " + Thread.interrupted()); System.out.println("CurrentThread#interrupted: " + Thread.currentThread().isInterrupted()); System.out.println("ConsumerThread#interrupted: " + ct.isInterrupted()); System.out.println(); System.out.println("# interrupt consumerThread ..."); ct.interrupt(); System.out.println("Thread#interrupted: " + Thread.interrupted()); System.out.println("CurrentThread#interrupted: " + Thread.currentThread().isInterrupted()); System.out.println("ConsumerThread#interrupted: " + ct.isInterrupted()); } private static class Consumer implements Runnable { private final BlockingQueue<Object> queue; public Consumer(BlockingQueue<Object> queue) { this.queue = queue; } public void run() { while (true) { try { queue.take(); } catch (InterruptedException e) { System.out.println("-> ConsumerThread is interrupted."); break; } } } } private static class InterruptConsumer implements Runnable { private final BlockingQueue<Object> queue; public InterruptConsumer(BlockingQueue<Object> queue) { this.queue = queue; } public void run() { while (true) { try { queue.take(); } catch (InterruptedException e) { System.out.println(">> InterruptConsumerThread is interrupted."); Thread.currentThread().interrupt(); System.out.println("-> interrupt!"); break; } } } } }
出力結果は以下のようになる。
----- check by ConsumerThread -----------------------
# no interrupt ...
Thread#interrupted: false
CurrentThread#interrupted: false
ConsumerThread#interrupted: false# interrupt currentThraed ...
Thread#interrupted: true
CurrentThread#interrupted: false
ConsumerThread#interrupted: false# interrupt consumerThread ...
Thread#interrupted: false
CurrentThread#interrupted: false
ConsumerThread#interrupted: false
- > ConsumerThread is interrupted.
----- check by InterruptConsumerThread -----------------------
# no interrupt ...
Thread#interrupted: false
CurrentThread#interrupted: false
ConsumerThread#interrupted: false# interrupt currentThraed ...
Thread#interrupted: true
CurrentThread#interrupted: false
ConsumerThread#interrupted: false# interrupt consumerThread ...
Thread#interrupted: false
CurrentThread#interrupted: false
ConsumerThread#interrupted: true
- > InterruptConsumerThread is interrupted.
- > interrupt!