倭マン's BLOG

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

Groovy で Producer-Consumer パターン

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』に載っているデザインパターンを Groovy/GPars で書こうシリーズ、今回は Producer-Consumer パターン。 Producer-Consumer パターンはやるべき仕事を見つけることとその仕事を実行することを互いに隔離し分離します(『Java並行処理プログラミング ―その「基盤」と「最新API」を究める―』) 非同期処理の基本であり真髄ってところでしょうか。

このデザインパターンも、Guarded Suspension パターンと同じく、java.util.concurrent パッケージに含まれる BlockingQueue インターフェースとそのサブタイプを使えば簡単に実装できます。

サンプル・コード


ここで作成するサンプルのソースは

  • MakerActor クラス
  • 実行スクリプト(テーブル、eater を含む)

です。 ケーキの Producer である MakerActor がケーキ(String オブジェクト)を作ってテーブル(BlockingQueue オブジェクト)に置き、ケーキの Consumer である eater がテーブルに置かれたケーキを食べます。 eater も Actor として作成しますが、実装が簡単なので実行スクリプトに直接書いてます。

MakerActor クラス

MakerActor は順々に番号が付けられたケーキを作ります。 番号は全ての MakerActor インスタンスで共通につけられるようにします:

import groovyx.gpars.actor.DefaultActor
import java.util.concurrent.BlockingQueue

class MakerActor extends DefaultActor{
    
    private static int ID = 0
    
    private final String name
    private final BlockingQueue<String> table
    private final Random random
    
    MakerActor(String name, BlockingQueue<String> table, long seed){
        this.name = name
        this.table = table
        this.random = new Random(seed)
    }

    @Override
    protected void act() {
        loop{
            Thread.sleep this.random.nextInt(1000)
            def cake = "[ Cake No. ${nextId()} by $name ]"
            this.table.put(cake)
            println "$name puts $cake"
        }
    }
    
    private static synchronized nextId(){
        return ID++
    }
}

実行スクリプト

実行スクリプトでは Consumer 役の eater を Actors#actor() によって作成しています。 また、ケーキを置くテーブルは BlockingQueue (の実装クラスである ArrayBlockingQueue)として作成しています。 『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』のサンプルと少々違って、標準出力への put/take のログ書き出しをテーブルの外で行っているので、ログ出力だけを見ると作られてるケーキの番号と順番が一致しない可能性もあります。 修正するには BlockingQueue のサブクラスなりラップクラスなりを作る必要があるので、ここでは妥協。

import java.util.concurrent.BlockingQueue
import java.util.concurrent.ArrayBlockingQueue
import groovyx.gpars.actor.Actor
import groovyx.gpars.actor.Actors

BlockingQueue<String> table = new ArrayBlockingQueue<>(3)

def actors = []
actors << new MakerActor('MakerActor-1', table, 31415L).start()
actors << new MakerActor('MakerActor-2', table, 92653L).start()
actors << new MakerActor('MakerActor-3', table, 58979L).start()
actors << eaterActor('EaterActor-1', table, 32384L)
actors << eaterActor('EaterActor-2', table, 62643L)
actors << eaterActor('EaterActor-3', table, 38327L)

actors*.join()

Actor eaterActor(String name, BlockingQueue<String> table, long seed){
    return Actors.actor{
        def random = new Random(seed)
        loop{
            def cake = table.take()
            println "$name takes $cake"
            Thread.sleep(random.nextInt(1000))
        }
    }
}

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

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


プログラミングGROOVY

プログラミングGROOVY