2011/12/04

InterruptedExceptionは何のためにある?

InterruptedExceptionがスローされるメソッドは結構ありますが、
停止状態(ブロック状態)にあるスレッドを再開するためにInterruptedExceptionをスローするというのが基本的な考え方であって、スレッドをinterruptするのではありません
言い換えると、InterruptedExceptionは、停止状態(ブロック状態)にある処理を一度キャンセルしてスレッドの処理を再開させるためにあっても、必ずしもブロックの原因となった処理を繰り返し実行してはいけないというわけではないと考えています。

次のようなプログラムを考えてみます。
Queueから要素を取り出すスレッド(11行目で非デーモンに設定)が起動されます。このスレッドは非デーモンなので、mainスレッドが終了してもアプリケーションは即座に終了しないようになっています。
一方、mainスレッドではQueueに10個の要素を挿入し、11個目の要素の挿入で処理を終えるように指示しています。
なお実験的に20行目のコメントを外して、InterruptedExceptionをスローさせることができます。
package queue;

import java.util.concurrent.LinkedBlockingQueue;

public final class A {
 private static final LinkedBlockingQueue<Item> queue = new LinkedBlockingQueue<Item>();

 public static void main(String[] args) {
  //QueueからItemを取りだすスレッドを起動
  final Thread thread = new Thread(new Service());
  thread.setDaemon(false);
  thread.start();

  //QueueにItemを入れる
  for(int i=0; i<10; i++){
   queue.add(new Item(false, "Msg"+String.valueOf(i)));
  }

  //BlockingQueue#takeでInterruptedExceptionをスローさせる
//  thread.interrupt();

  //最後のItemを入れる
  queue.add(new Item(true, null));
 }

 //Item
 static class Item {
  private final boolean end; //trueなら最後のItemであることを示す
  private final String msg;

  Item(boolean end, String msg){
   this.end = end;
   this.msg = msg;
  }

  boolean isEnd(){
   return this.end;
  }

  String getMsg(){
   return this.msg;
  }
 }

 //QueueからItemを取りだすスレッド
 static class Service implements Runnable {
  public void run() {
   boolean roop = true;
   while(roop){
    try {
     final Item item = queue.take(); //throws InterruptedException
     if(item.isEnd()==true){
      roop = false;    //ループを抜ける
     }else{
      System.out.println(item.getMsg());
     }
    } catch (InterruptedException e) {
     System.out.println("InterruptedException was thrown.");
//     roop = false;
    }
   }
  }
 }
}
このスレッドでQueueにある要素をすべて処理しなければならないと考えるならば、InterruptedExceptionを無視して処理を継続します。たとえば、LoggingのようにすべてのLogを吐き出したいときにはInterruptedExceptionを無視することを選択するでしょう。
逆に、すべての要素を処理する必要がないスレッドの場合は、InterruptedExceptionをキャッチして、次の処理に進めるか、あるいはスレッドを即時に終了することを選択することもできます。上記プログラムの場合、59行目のコメントを外せば、whileループを抜けて次の処理に進むことになります。
InterruptedExceptionのスローによって再開されたスレッドにおいて、どのように対応するかはユーザが自由に決めてもいいわけです。予期しないInterruptionなら無視し(NOP:NO Operationとする)、意図したInterruptionなら次の処理に進めることもできます。
一口で言ってしまうとケースバイケースだと考えています。ただ、対象のスレッドがどの処理を実行中であるのかを把握できていないとThread#interruptが効果的に働かないケースが多いのではないかと思うのです。また予期しないInterruptedExceptionがスローされる可能性もないわけではありません。

0 件のコメント:

コメントを投稿