JavaのExecutorフレームワークで例外処理

JavaのExecutorフレームワークでCallableを使った例外処理アイキャッチプログラム
スポンサーリンク

昔(Java1.4の頃)はJavaでThreadを使おうとすると、Threadを継承したクラスを作ったりRunnableを実装したクラスを作ったりして、直接Threadを使っていました。

しかし、Java5からはExecutorフレームワークという便利なものが作られたようで、最近は直接Threadを使うのではなくExecutorフレームワークを使うのが普通なようです。

最近できたフレームワークらしく実装そのものは簡単なのですが、例外処理でちょっと調査が必要になったのでそのへんを備忘録としてまとめておきます。

Executor#executeを使う場合

スレッドを実行した結果の戻り値が必要ない場合は、executeメソッドを使います。この場合、タスクを渡したexecuteメソッドから例外が投げられるようです。

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class MainForExecute {
	public static void main(String[] args) throws Exception {
		var pool = Executors.newWorkStealingPool();
		try {
			for (int i = 0; i < 20; i++) {
				try {
					// 例外はexecuteメソッドで投げられる
					pool.execute(new Task());
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		} finally {
			pool.shutdown();
			// すべてのスレットが終了するか、指定時間経過するまで待つ
			pool.awaitTermination(1, TimeUnit.MINUTES);
		}

	}

	private static class Task implements Runnable {
		@Override
		public void run(){
			var random = new Random();
			int result = random.nextInt(100);
			System.out.println(Thread.currentThread().getName() + " : " + result);
			if (result < 30) {
				// runメソッドは検査例外を投げられないので非検査例外を投げる
				throw new RuntimeException("result is " + result);
			}

		}
	}
}

マルチスレッドではなくシングルスレッドで処理を行っているように見えますが、実行結果を見るとちゃんと複数のスレッドで処理が行われているようです。

実行結果、ちゃんと複数のスレッドで実行されている

また、複数の例外が発生した場合もそれぞれのスタックトレースが表示されます。

ExecutorService#submitを使う場合

スレッドの実行結果が必要な場合はsubmitメソッドを利用します。このメソッドは戻り値としてFutureのインスタンスを返すのですが、Futureインスタンスから値を取得するタイミングで例外を投げる様になっています。

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class MainForSubmit {

	public static void main(String[] args) throws Exception{
		var pool = Executors.newWorkStealingPool();
		try {
			var list = new ArrayList<Future<Integer>>();
			for (int i = 0; i < 20; i++) {
				// 実行結果をリストに格納
				var future = pool.submit(new MyTask());
				list.add(future);
			}
			// すべての実行結果を表示
			for (Future<Integer> result : list) {
				try {
					// 発生した例外はgetメソッドを実行したときに投げられる
					var value = result.get();
					System.out.println("result : " + value);
				} catch (ExecutionException ee) {
					ee.getCause().printStackTrace();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		} finally {
			pool.shutdown();
			// すべてのスレットが終了するか、指定時間経過するまで待つ
			pool.awaitTermination(1, TimeUnit.MINUTES);
		}

	}
	private static class MyTask implements Callable<Integer> {

		@Override
		public Integer call() throws Exception{
			var random = new Random();
			int result = random.nextInt(100);
			if (result < 30) {
				// callは検査例外を投げられる
				throw new Exception("result is " + result);
			}
			return result;
		}
		
	}

}

一度実行結果をリストに格納後、すべてのFutureインスタンスからgetメソッドで値を取り出しています。getを実行する際、Taskのcallメソッドで例外が発生していたらExecutionExceptionが投げられる仕組みです。

executeを使う場合よりコードが複雑ではありますが、私としてはsubmitを使うほうが理解しやすかったです。executeはなんでこのコードでマルチスレッドなのか、納得ができていません・・・。

おすすめ

せっかくJavaを勉強しているのなら、資格を取得してみませんか?OCJP(Oracle Certified Java Programmer) SilverはJavaの基本を学ぶのに適した資格です。初心者はもとより、中堅の人でも普段使わない機能を勉強するきっかけになると思います。

残念なことに試験は有料で、しかも結構高い(税抜26,600円)です。

しかし、お勤めの会社によっては費用を負担してくれるところもあるでしょうし、場合によっては報奨金まで出してくれるはずです。(私の会社は初回の試験料負担+報奨金を出してくれました)

私のおすすめな勉強方法は徹底攻略Java SE 11 Silver問題集を読むことです。問題集ですが、解説が充実しています。

徹底攻略Java SE 11 Silver問題集[1Z0-815]対応 徹底攻略シリーズ | 志賀 澄人 | 工学 | Kindleストア | Amazon
Amazonで志賀 澄人の徹底攻略Java SE 11 Silver問題集[1Z0-815]対応 徹底攻略シリーズ。アマゾンならポイント還元本が多数。一度購入いただいた電子書籍は、KindleおよびFire端末、スマートフォンやタブレットなど、様々な端末でもお楽しみいただけます。

大体30時間くらいの勉強で合格できました。

コメント

タイトルとURLをコピーしました