倭マン's BLOG

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

Groovy で Worker Thread パターン

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』に載っているデザインパターンを Groovy/GPars で書こうシリーズ、今回は Worker Thread パターン。 Worker Thread パターンも java.util.concurrent パッケージのクラスを使えば実装するのは簡単です。 というか、Executor や ExecutorService を使うと Worker Thread パターンと Thread-Per-Message パターンの違いがあんまりよくわかんないですね。 『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』によると、この2つのパターンの違いは、処理を行うスレッドを逐次作成するのか?アプリケーションの起動時に作成しておくのか?という違いだそうなので、これらは ExecutorService のインスタンスとしてどれを使うのか?で切り替えられれば、それ以外のコードは同じになってもおかしくはなさそうですね。

サンプル・コード


ではサンプル・コード。 作成するソースは

  • Request クラス リクエストをあらわす。 Runnable を実装
  • ClientActor リクエストを作成して送信する
  • 実行スクリプト

リクエストを受け取って処理を実行するのは ExecutorService (Executor) クラスに任せます。

Request クラス

リクエストを表すクラス。 Runnable を実装することで処理自体も記述されています:

class Request implements Runnable{
    
    private static final Random RANDOM = new Random()
    
    private final String name
    private final int number
    
    Request(String name, int number){
        this.name = name
        this.number = number
    }
    
    @Override
    void run(){
        println "${Thread.currentThread().name} executes $this"
        Thread.sleep 1000
    }
    
    @Override
    String toString(){
        return "[ Request from $name No. $number ]"
    }
}

ClientActor クラス

ClientActor は、リクエストを作成して ExecutorService オブジェクトに送信し続けます。 GPars の Actor を用いて実装:

class ClientActor extends DefaultActor{
    
    private static final Random RANDOM = new Random()
    
    private final String name
    private final ExecutorService exeService    // Executor で充分だったかな?
    
    ClientActor(String name, ExecutorService exeService){
        this.name = name
        this.exeService = exeService
    }
    
    @Override
    void act(){
        int i = 0
        loop{
            try{
                def request = new Request(this.name, i)
                this.exeService.execute(request)
                Thread.sleep 1000

            }catch(InterruptedException | RejectedExecutionException e){
                println "$name : $e"
            }
            i++
        }
    }
}

実行スクリプト

実行スクリプトで ExecutorService オブジェクトの作成と ClientActor オブジェクトの作成を行っています。 今回もThread-Per-Message パターンの場合と同じく GParsExecutorPool が提供する ExecutorService を使用しています。 全く同じ使い方もアレなので、プールのスレッド数と ThreadFactory を指定するようにしてみました:

import groovyx.gpars.GParsExecutorsPool
import java.util.concurrent.Executors

// スレッドプールのスレッド数と ThreadFactory を設定
GParsExecutorsPool.withPool(10, Executors.defaultThreadFactory()) {
    def actors = []
    actors << new ClientActor('Alice', it).start()
    actors << new ClientActor('Bobby', it).start()
    actors << new ClientActor('Chris', it).start()
    actors*.join()
}

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

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


プログラミングGROOVY

プログラミングGROOVY