第五章 (3): スレッドの割り込み

スレッドの割り込みについて。BlockingQueueのputやtakeは、InterruptedExceptionをスローする。例えばtakeの場合は、次の要素が無くてスレッドが待機状態になっている間、別スレッドが割り込みを行うとスローされる。割り込みされたスレッドは、出来るだけ早くブロックを抜ける努力をする。努力をするという点がポイントで、InterruptedExceptionは何かを強制するものではない。別スレッドがinterruptしたことを、そのスレッドに通知するのが目的である。スレッドAがスレッドBにinterruptするということは、「あなたの都合が良いときに今やっている処理を終えて下さい」というリクエストの意思表示である。BlockingQueueのtakeの場合は、待機状態にあるスレッドがInterruptedExceptionをスローすることにより、take処理を強制終了させるのか、それともまだ待機するのかをInterruptedExceptionをキャチする側で記述することになる。

通常、InterruptedExceptionが発生した場合の手順は以下の2つ。

  1. catchせずにそのままthrowする、もしくは終了処理を行った後に再throwすることにより、クライアント側に処理を委譲する
  2. 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!