『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』に載っているデザインパターンを Groovy/GPars で書こうシリーズ、今回は Future パターン。 このパターンでは、「引換券を、お先にどうぞ」と言うのが的を射ているように、まだ終わっていない処理の結果をあたかも既に得ているかのようにコードを書いていけます。 このパターンも java.util.concurrent パッケージにある Future インターフェースなどを使えば大した手間無く実装できます。
ここでのサンプルは、『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』に書かれているサンプルと、以下の点を変えてあります(基本は同じですが):
- クライアント(実行スクリプト)がデータとして使用する型は Data ではなく String に
- FutureTask を使用した実装ではなく ExecutorService#submit() を使用した実装に
- 時間のかかる処理は RealData の中でではなく ExecutorService#submit() に渡す Callable の中で
ちなみに ExecutorService インターフェースに定義されているメソッドのうち Future オブジェクトを返すものは
- invokeAll : List<Future<T>>
- submit : Future<T>
があります。
サンプル・コード その1 : java.util.concurrent の Future を使うサンプル
Future パターンを java.util.concurrent の Future インターフェースなどで実装したサンプル。 作成するソースは
- Host クラス 時間のかかる処理の開始と Future オブジェクトの作成
- 実行スクリプト
です。
Host クラス
このクラスは『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』とは実装を変えています。 公開 API としては request() メソッドの返り値として Future<String> オブジェクトを返すようにしています。 内部実装としては、フィールドに ExecutorService オブジェクトを持たせ、ExecutorService#submit() メソッドによって Future オブジェクトを生成しています。 時間のかかる処理は Callable インターフェース(返り値のある Runnable インターフェースみたいなもの)を使って、無名クラスとして作成しています:
import java.util.concurrent.Callable import java.util.concurrent.ExecutorService import java.util.concurrent.Future class Host { private final ExecutorService exeService Host(ExecutorService exe){ this.exeService = exe } Future<String> request(int count, String c){ println " request($count, $c) BEGIN" Future<String> result = this.exeService.submit(new Callable<String>(){ @Override String call() { println " making String($count, $c) BEGIN" char[] data = new char[count] (0..<count).each{ data[it] = c.charAt(0); Thread.sleep 100 } println " making String($count, $c) END" return String.valueOf(data) } }) println " request($count, c) END" return result } }
実行スクリプト
実行スクリプトはあまり変えてませんが、Host クラスが ExecutorService を使うようにしたので GParsExecutorPool を使っています:
import groovyx.gpars.GParsExecutorsPool println 'main BEGIN' GParsExecutorsPool.withPool{ def host = new Host(it) def result1 = host.request(10, 'A') def result2 = host.request(20, 'B') def result3 = host.request(30, 'C') println 'main other Job BEGIN' Thread.sleep 2000 println 'main other Job END' println "result1 = ${result1.get()}" println "result2 = ${result2.get()}" println "result3 = ${result3.get()}" } println 'main END'
サンプル・コード その2 : GPars の DataFlow を使うサンプル
さて、今まで Groovy は使えども GPars はほとんど使わず、使ってもちゃっちい Actor 実装程度でしたが、やっと別の GPars ライブラリを使うときがw ということで、DataFlow を使って Future パターンを実装するサンプルを書いてみました。 ソースはこのスクリプト1つ:
import static groovyx.gpars.dataflow.DataFlow.task import groovyx.gpars.dataflow.DataFlowVariable println 'main BEGIN' // 変数初期化 def result1 = new DataFlowVariable<String>() def result2 = new DataFlowVariable<String>() def result3 = new DataFlowVariable<String>() // 処理のかかる計算を実行開始 task{ result1 << buildResult(10, 'A') } task{ result2 << buildResult(20, 'B') } task{ result3 << buildResult(30, 'C') } println 'main other Job BEGIN' Thread.sleep 2000 println 'main other Job END' // 処理結果を取得できたら行う処理 println "result1 = $result1.val" println "result2 = $result2.val" println "result3 = $result3.val" println 'main END' def buildResult(int count, String s){ println " making String($count, $s) BEGIN" char[] data = new char[count] (0..<count).each{ data[it] = s.charAt(0); Thread.sleep 100 } println " making String($count, $s) END" return String.valueOf(data) }
DataFlow のライブラリを使ってる箇所は
- Future オブジェクトの代わりに、DataFlowVariable オブジェクトを使用
- Executor#submit() メソッドの代わりに、groovyx.gpars.dataflow.DataFlow.task() メソッドで計算処理を開始
- Future#get() メソッドの代わりに、DataFlowVariable.val (getVal() メソッド) によって結果を取得
といった感じです。

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/03/21
- メディア: 大型本
- 購入: 15人 クリック: 287回
- この商品を含むブログ (199件) を見る

- 作者: 関谷和愛,上原潤二,須江信洋,中野靖治
- 出版社/メーカー: 技術評論社
- 発売日: 2011/07/06
- メディア: 単行本(ソフトカバー)
- 購入: 6人 クリック: 392回
- この商品を含むブログ (152件) を見る