2012年04月11日

java.util.concurrent 〜Executor その3〜


さて、今回は複数の非同期処理を行う場合です。RunnableとThreadを用いて3本以上のスレッドの待ち合わせをするのは割と面倒だったりするのですが、ExecutorServiceを使用すると簡単です。また、ExecutorServiceはスレッドプールの機能も持ち合わせているので好きなようにリソースを最適化することができます。

では例によってサンプルソースを見ます。今回は非同期処理として複数の子スレッドでHTTP送信を行い、メインスレッドでは非同期処理が全て終わり次第結果を出力するようにします。HTTP送信など、コストが高い処理を順不同で大量に行う必要があり、その結果をマージしたい場合を想定します。

メインメソッド
public static void main(String[] args) {

	ExecutorService executor = null;
	try{
		//固定(3本)のスレッドプールを有するExecutorServiceを生成する
		executor = Executors.newFixedThreadPool(3);

		//タスクの生成しリストに詰める
		HttpGetTask task1 = new HttpGetTask("http://localhost:8080/DummyServer/test1.html");
		HttpGetTask task2 = new HttpGetTask("http://localhost:8080/DummyServer/test2.html");
		HttpGetTask task3 = new HttpGetTask("http://localhost:8080/DummyServer/test3.html");
		List<Callable<String>> tasks = new ArrayList<Callable<String>>();
		Collections.addAll(tasks, task1,task2,task3);

		List<Future<String>> resultList = null;
		try {
			//全てのタスクを実行
			resultList = executor.invokeAll(tasks);
		} catch (InterruptedException e) {
			e.printStackTrace();
			return;
		}

		//結果を表示
		for(Future<String> result : resultList){
			try {
				System.out.println(result.get());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}

	}finally{
		//使い終わったらshutdownしておく
		if(executor != null){
			executor.shutdown();
		}
	}
}

非同期タスククラス
/**
 * HTTP GETを行う非同期処理。
 * @author ragtimer
 *
 */
public class HttpGetTask implements Callable<String> {

	/**
	 * 接続先URL
	 */
	private String url;

	/**
	 * コンストラクタ
	 */
	public HttpGetTask(String url){
		this.url = url;
	}


	@Override
	public String call() throws Exception {
		System.out.println("HttpGetTask start...");

		//url宛てにHTTPGETを送るだけ
		HttpClient httpclient = new DefaultHttpClient();
		HttpGet httpGet = null;
		String result = null;
		try {
			httpGet = new HttpGet(url);
			HttpResponse response = httpclient.execute(httpGet);
			HttpEntity httpEntity = response.getEntity();
			result = EntityUtils.toString(httpEntity);
		} finally {
            if (httpGet != null) {
                httpGet.abort();
            }
        }
		System.out.println("HttpGetTask end...");

		return result;
	}

}


ではソースを詳しく見ていきます。

・ExecutorServiceを作成
executor = Executors.newFixedThreadPool(3);
Executors.newFixedThreadPool(int)を使用します。引数はプールするスレッド数です。これでこのExecutorServiceでスレッドプールが勝手に適用されます。仮にスレッドが枯渇した場合はスレッドが空くまで待機してくれます。

・非同期処理を複数作成
HttpGetTask task1 = new HttpGetTask("http://localhost:8080/DummyServer/test1.html");
HttpGetTask task2 = new HttpGetTask("http://localhost:8080/DummyServer/test2.html");
HttpGetTask task3 = new HttpGetTask("http://localhost:8080/DummyServer/test3.html");
List<Callable<String>> tasks = new ArrayList<Callable<String>>();
Collections.addAll(tasks, task1,task2,task3);
Callableを実装したHttpGetTaskを複数生成し、Listに詰めています。HttpGetTaskは生成時に渡されたurlにHTTP GETを送り、ボディを返却するだけの処理です。なお、HTTP送信にはapacheのHTTPClientを使用してます。対向のサーバローカルに適当に立てたTomcatです。

・非同期処理の実行
resultList = executor.invokeAll(tasks);
非同期処理を詰めたリストを引数にExecutorService.invokeAll(Collection>)を呼び出します。引数はFutureオブジェクトのリストで返却されます。

・結果の表示
for(Future<String> result : resultList){
	try {
		System.out.println(result.get());
	} catch (InterruptedException e) {
		e.printStackTrace();
	} catch (ExecutionException e) {
		e.printStackTrace();
	}
}
Futureをループで回しながら結果を取得していきます。たったこれだけで待ち合わせができるのは素晴らしいですね。

次回はスケジュール処理をやります。
posted by sandman at 23:20| Comment(0) | Java | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

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


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

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