2012年04月11日

java.util.concurrent 〜Executor その2〜


さて、今回は戻り値を取得できるExecutorServiceのメソッドを使います。Runnableが提供するAPIはvoid run()ですので、Runnableでは非同期処理の戻り値を返すことができません。そこで、代わりにCallable<V>を使用します。Callableは引数を持たず、総称型の戻り値を持つcall()というメソッドを持ちます。Runnableと異なり、Exceptionを返すこともできます。このCallableをRunnableの代わりに使用します。

非同期処理の戻りを受け取るにはもうひとつ重要なインターフェースがあります。それはFutureです。Futureは非同期処理の結果を表します。あれ?非同期計算の結果はCallableの総称型で定義した戻り値じゃないの・・?と思いますが、Futureは非同期処理の終了をチェックし、自動的に同期をとってくれます。言葉ではわかりにくいのでサンプルを見てみます。

public static void main(String[] args) {

	//実行するタスクをCallableで作成
	Callable<Result> task = new Callable<Result>(){

		//Runnableと異なり、Callableは戻り値を返却することができます。
		//戻り値は総称型で定義します
		@Override
		public Result call() throws Exception {

			//三秒待つ(java.util.concurrent.TimeUnitを使用)
			try {
				TimeUnit.MILLISECONDS.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			Result result = new Result();
			Date date = new Date();
			result.setResultDate(date);
			return result;
		}

	};

	ExecutorService executor = null;
	try{
		//シングルスレッドを生成するためのExecutorServiceを作成する
		executor = Executors.newSingleThreadExecutor();

		//Futureオブジェクトはsubmitされた非同期計算の結果を取得するためのインターフェイス
		//非同期処理との同期が可能
		Future<Result> future = executor.submit(task);

		Result result = null;
		try {
			//非同期処理の結果を受け取る ※終了していない場合はここで待ってくれる
			result = future.get();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}

		//結果表示
		System.out.println(result.getResultDate());

	}finally{
		//使い終わったらshutdownしておく
		if(executor != null){
			executor.shutdown();
		}
	}
}
・処理結果クラス
/*
 * 処理結果を表すクラス
 *
 */
public class Result {

	//処理結果日時
	private Date resultDate;

	public Date getResultDate() {
		return resultDate;
	}

	public void setResultDate(Date resultDate) {
		this.resultDate = resultDate;
	}

}

ソースを詳しく見ていきます。
・非同期処理の生成
Callable<Result> task = new Callable<Result>(){

	//Runnableと異なり、Callableは戻り値を返却することができます。
	//戻り値は総称型で定義します
	@Override
	public Result call() throws Exception {

		//三秒待つ(java.util.concurrent.TimeUnitを使用)
		try {
			TimeUnit.MILLISECONDS.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Result result = new Result();
		Date date = new Date();
		result.setResultDate(date);
		return result;
	}

};
前回とは異なり、Callableの匿名クラスで定義しています。総称型の戻り値はResultという処理結果を表すクラスです。単に非同期処理を行った時刻を保持しているだけのクラスですね。

・タスクを実行
Future<Result> future = executor.submit(task);
非同期処理の実行は、ExecutorService.submit(Callable)を使用します。このメソッドは戻り値にFutureを返しています。Vには、Callableに戻り値としてしかけたResultを指定してやります。もちろん、Futureが返却された時点では非同期処理そのものが終わっているわけではありません。

・非同期処理の結果を取得
result = future.get();
Future.get()で非同期処理の結果を受け取ります。このメソッドが呼び出されたタイミングで、非同期処理が完了している保証は全くありませんが、前述の通りFutureは自動的に同期をとり、完了していない場合は待機してくれます。戻り値はこの場合Resultになります。このFutureオブジェクトがあれば、好きな場所で非同期処理の結果を受け取ることができるわけです。これは非常に便利ですね。

次回は複数のタスクの処理です。
posted by sandman at 21:55| Comment(0) | Java | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。