倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Groovy で Future パターン

増補改訂版 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言語で学ぶデザインパターン入門 マルチスレッド編

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編


プログラミングGROOVY

プログラミングGROOVY