Java Magazineの巻末には、毎号「Fix This」という記事が連載されている。壊れたコードを提示して、正しい修正方法を答えさせるというクイズ形式の演習問題だ。問題の答えは次号に掲載される形式で、解説にはきれいなコードを書くためのヒントが含まれている。自分の力試しに、あるいはスキル向上の教材として役立つだろう。ここでは、第15号に掲載された問題と、第16号に掲載されたその答えをご紹介しよう。
エラー・ロギングの問題を解決せよ(Vol.15:P51)
1 問題
ThreadPoolExecutor 内でRunnable を実行中にスローされる例外を正しくロギングしたいと考えています。
2 コード
次のコードを思いつきましたが、これでは、Runnable をsubmit するすべてのコールサイトを修正しなければなりません。もっと簡単な方法はないでしょうか。
final Future<?> runnableSubmission = es.submit(new Runnable() {
@Override
public void run() {
int computed = 1 / 0;
}
});
es.submit(new Runnable() {
@Override
public void run() {
try {
runnableSubmission.get();
} catch (InterruptedException | ExecutionException e) {
LOG.log(Level.SEVERE, "Exception while executing task!", e);
}
}
});
3 正しい修正方法はどれですか
- ThreadPoolExecutor にThreadFactory を追加する。つまり、作成したスレッドで、UncaughtException Handler を設定する。
- ThreadPoolExecutor を継承し、afterExecute メソッドをオーバーライドする。
- ThreadPoolExecutorを継承し、Future> submit(Runnable task)メソッドをオーバーライドして、それぞれのRunnable の後にチェック・タスクを追加する。
- ThreadPoolExecutor を継承し、ExecutorService インタフェースに宣言されている3種類のsubmit メソッドすべてをオーバーライドする。
解答(Vol.16:P65)
14 年1 月/2 月号では、ルーマニアの多言語開発者であるAttila Balazs 氏から、エラー・ロギングに関する課題が出題されました。Runnable をsubmit するすべてのコールサイトを修正しなければならないコードが示されたうえで、もっと簡単な方法が問われました。正解は2 番(ThreadPoolExecutor を継承し、afterExecute メソッドをオーバーライドする)です。このメソッドのjavadoc では、例外のロギングを例に挙げて、afterExecute メソッドをオーバーライドする理由について説明しています。
この例のSystem.out.println を適切なロガー呼出しに変えるだけで作業は終了です。選択肢の1 番は、例外が未処理のままでは残らないためうまく行きません。例外がThreadPoolExecutor にキャッチされ、UncaughtExceptionHandler まで届きません。3 番と4 番は、エグゼキュータへのタスクの送信方法によってはうまく行かない場合があります。3 番の方法では4 番に挙げられているメソッドが対象にならず、4 番の方法ではvoid execute(Runnable command) メソッドが対象になりません。submit 関連のメソッドをオーバーライドするという実装方法では、すべての状況に対応するために、これら4 つのメソッドをすべてオーバーライドする必要があります。