- PR -

Executors(ThreadPoolExecutor)でのRuntimeExceptionのキャッチ

1
投稿者投稿内容
よろしくお願いします。
常連さん
会議室デビュー日: 2006/01/17
投稿数: 21
投稿日時: 2009-02-26 03:53
Java5から新しく追加された「java.util.concurrent」パッケージを使用してスレッド処理の勉強をしています。
いろいろわかってきたところで、
 「別スレッドで例外(RuntimeException系)が発生した場合、独自の例外処理を行うにはどうしたらいいのだろう?」
という疑問にかられました。そこで、以下のようなプログラムを組んでみたのですが、どうにもうまく動作しません。
※自作の「OrgUncaughtExceptionHandler」クラスの例外対応処理(uncaughtExceptionメソッド)が呼び出されない。

import java.util.*;
import java.util.concurrent.*;

public class java5_Excecutor {
  static class Counter implements Runnable {
    private Thread.UncaughtExceptionHandler p_defUe;
    
    public Counter(Thread.UncaughtExceptionHandler defUe) {
      this.p_defUe = defUe;
    }
    
    public void run() {
      try {
        Thread currentThread = Thread.currentThread();
        Thread.UncaughtExceptionHandler ue = currentThread.getUncaughtExceptionHandler();
        
        if (ue == null || !(ue instanceof OrgUncaughtExceptionHandler)) {
          currentThread.setUncaughtExceptionHandler(this.p_defUe);
          System.out.println(" " + currentThread.getName() + ":設定");
        } else {
          System.out.println(" " + currentThread.getUncaughtExceptionHandler().getClass().getName() + ":確認");
        }
        
        for (int i = 0; i < 5; i++) {
          System.out.println(currentThread.getName() + ":" + i);
          
          if (i == 3) {
            System.out.println(" " + currentThread.getName() + ":例外");
            throw new RuntimeException();
          }
          
          Thread.sleep(1000);
        }
      } catch (InterruptedException e) {
      }
    }
  }

  static public void main(String args[]) {
    ExecutorService ex = null;
    
    try {
      ex = Executors.newFixedThreadPool(1);
      
      for (int i = 0; i < 3; i++) {
        Counter th = new Counter(new OrgUncaughtExceptionHandler());
        ex.submit(th);
      }
    } finally {
      ex.shutdown();
    }
    
  }
  
  static class OrgUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    public void uncaughtException(Thread t, Throwable e) {
      System.out.println(t.getName() + ":キャッチ(" + e.toString() + ")");
    }
  }
}

いろいろ調べてみたのですが、どうにも手詰まりで…。
何かご存知の方がいらっしゃったら、お知恵を拝借できないでしょうかm(_ _)m
わたなべ
大ベテラン
会議室デビュー日: 2007/12/09
投稿数: 123
お住まい・勤務地: 札幌
投稿日時: 2009-02-26 08:03
どこを参考にしてこのサンプルを作成したのか解りませんが、ちゃんと解説してある本やページを参考した方がいいと思います。
1. Runnableを使わずにCallable<T> を使用する
⇒ Runnableは戻り値がvoidで例外をthrowsできませんが、Callableは可能
2. submitの戻り値であるFuture<V> を使用する
http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/Task.html
⇒ タスクの実行状態を保持するオブジェクトになります

例外の処理ですが、Futureから結果を取得するときに発生するExecutionExceptionをcatchする事で実現できます。
http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/java/util/concurrent/ExecutionException.html
よろしくお願いします。
常連さん
会議室デビュー日: 2006/01/17
投稿数: 21
投稿日時: 2009-02-26 19:58
原因がわかりました。

〜原因〜
「java.util.concurrent.FutureTaskクラス」中に定義されているprivate innerクラスである「Syncクラス」の「innerRunメソッド」でThrowableクラスインスタンスがtry〜catchされている為、RuntimeExceptionもcatchされてしまっている為に、自作ハンドラーへ処理が移行しない。

〜わたなべ様〜
ご教授ありがとうございます。m(_ _)m
> どこを参考にしてこのサンプルを作成したのか解りませんが、ちゃんと解説してある本やページを参考した方がいいと思います。
おっしゃるとおりだと思います。
そこでなのですが、
  「java.util.concurrent」パッケージを使用してスレッド処理を行う場合、
 「Threadクラス」にある「setUncaughtExceptionHandlerメソッド」や
 「setDefaultUncaughtExceptionHandlerメソッド」を用いた独自例外処理は
 できません。
  その為、「java.util.concurrent」パッケージを使用してスレッド処理を
 行い独自例外処理を実装する場合、
  ・Callableを使用し処理結果(Future)から例外有無を判断し処理
  ・ThreadPoolExecutorクラスを継承した独自ThreadPoolExecutorクラスを
   定義し、afterExecuteメソッドをオーバーライドし例外有無を判断し処理
 といった方法があげられます。
といった感じで解説されているようなサイト/本をご存知であれば教えて頂けないでしょうか?


少し余談になるのですが、今回、私がどうやって原因を調べたのかというと、JDKに付属されているソース(src.zip)を追いかけて原因を特定しました。
あまり、ソースを読むのは得意でないため、結構、時間がかかってしまったのですが、わたなべ様はどのように調べたりされていますか?
もしよろしければ、あわせてご教授して頂けないでしょうか? m(_ _)m


引き続き、今回の問題をクリアしたサンプルを作成したいと思います。
わたなべ
大ベテラン
会議室デビュー日: 2007/12/09
投稿数: 123
お住まい・勤務地: 札幌
投稿日時: 2009-02-26 20:45
Java並行処理プログラミング ―その「基盤」と「最新API」を究める
http://www.amazon.co.jp/Java%E4%B8%A6%E8%A1%8C%E5%87%A6%E7%90%86%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E2%80%95%E3%81%9D%E3%81%AE%E3%80%8C%E5%9F%BA%E7%9B%A4%E3%80%8D%E3%81%A8%E3%80%8C%E6%9C%80%E6%96%B0API%E3%80%8D%E3%82%92%E7%A9%B6%E3%82%81%E3%82%8B%E2%80%95-Brain-Goetz/dp/4797337206/ref=sr_1_1?ie=UTF8&s=books&qid=1235648555&sr=8-1

concurrentフレームワークの開発者の本です。
並列処理に関しての基礎知識も含めて結構なボリュームですが、一読する価値のある本かと思います
よろしくお願いします。
常連さん
会議室デビュー日: 2006/01/17
投稿数: 21
投稿日時: 2009-02-27 01:10
〜わたなべ様〜
ご回答ありがとうございます。m(_ _)m
教えて頂いた本は是非、読んでみたいと思います。
※どうも、どこにも在庫がないようですが…orz...

自分がやりたかったことができたので、そのサンプルをアップします。
【やりたかったこと】
・「java.util.concurrent」パッケージを使用し並行処理を行う。
・並行処理中に例外が起こった場合、独自に定義した例外処理を実行する。

【サンプル】
import java.util.*;
import java.util.concurrent.*;

public class java5_Excecutor {
 static final private int LENGTH = 10;
 
 static public void main(String args[]) {
  /**
   * Callable経由
   * 独自例外処理を実行することができる。
   * 処理結果を受け取ることができる。
   */
  sample1();
  
  /**
   * Runnable経由
   * 独自例外処理を実行することができる。
   * 処理結果を受け取ることができない。
   * スレッド起動時に引数を設定し、実行後、引数として設定した値を受け取ることができる。
   */
  sample2();
 }
 
 
 
 static public void sample1() {
  ExecutorService ex = null;
  CompletionService<Integer> cs = null;
  
  try {
   ex = Executors.newFixedThreadPool(LENGTH);
   cs = new ExecutorCompletionService<Integer>(ex);
   
   for (int i = 0; i < LENGTH; i++) {
    cs.submit(new RandomWaitCall());
    System.out.println("スレッド起動完了");
   }
   
   for (int i = 0; i < LENGTH; i++) {
    Future<Integer> f = cs.take();
    
    try {
     int r = f.get();
     System.out.println("処理結果:" + r);
    } catch (ExecutionException ee) {
     System.out.println(" スレッド中で例外が発生しました。(" + ee.getCause().toString() + ")");
    }
   }
  } catch (InterruptedException ie) {
   System.out.println(" 割り込みエラーです。");
  } finally {
   ex.shutdown();
  }
 }
 
 static class RandomWaitCall implements Callable<Integer> {
  public Integer call() throws Exception {
   // 0〜5までで乱数を生成
   int r = (int)(Math.random() * 6);
   
   if (r == 0) {
    // 0の場合は擬似的に例外を発生させる。
    throw new RuntimeException("0が選択されました。(" + r + ")");
   }
   
   Thread.sleep(r * 1000);
   return r;
  }
 }
 
 
 
 static public void sample2() {
  ExecutorService ex = null;
  CompletionService<Integer> cs = null;
  
  try {
   ex = Executors.newFixedThreadPool(LENGTH);
   cs = new ExecutorCompletionService<Integer>(ex);
   
   for (int i = 0; i < LENGTH; i++) {
    cs.submit(new RandomWaitRun(), i);
    System.out.println("スレッド起動完了");
   }
   
   for (int i = 0; i < LENGTH; i++) {
    Future<Integer> f = cs.take();
    
    try {
     int r = f.get();
     System.out.println("呼出引数:" + r);
    } catch (ExecutionException ee) {
     System.out.println(" スレッド中で例外が発生しました。(" + ee.getCause().toString() + ")");
    }
   }
  } catch (InterruptedException ie) {
   System.out.println(" 割り込みエラーです。");
  } finally {
   ex.shutdown();
  }
 }
 
 static class RandomWaitRun implements Runnable {
  public void run() {
   // 0〜5までで乱数を生成
   int r = (int)(Math.random() * 6);
   
   if (r == 0) {
    // 0の場合は擬似的に例外を発生させる。
    throw new RuntimeException("0が選択されました。(" + r + ")");
   }
   
   try {
    Thread.sleep(r * 1000);
   } catch(InterruptedException ie) {
    throw new RuntimeException("スリープ中に例外が発生しました。");
   }
   return;
  }
 }
 
}

よろしくお願いします。
常連さん
会議室デビュー日: 2006/01/17
投稿数: 21
投稿日時: 2009-02-28 23:29
〜わたなべ様〜
ご教授頂いた書籍をなんとか入手すことができました。
目から鱗が沢山です。本当にありがとうございました。

ただ、私には理解しかねるような難しい箇所が複数あり、それでも、なんとか読み進めているのですが、自分の理解が正しいか自信がありません...。

http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=48422&forum=12&0
↑新しくスレッドを建て2つ質問しております。
もしよろしければ、ご意見を頂戴できないでしょうか?
1

スキルアップ/キャリアアップ(JOB@IT)