【デザインパターン】【非同期】Producer-Consumer パターン

■ Producer-Consumer パターン(生産者-消費者パターン)

 * Queue(キュー)を用意し、
   1つのプロセス(producer)が、データが生産できたらそのキューに追加。
   もう1つのプロセス(consumer)が、キューからデータを取り出し、処理をする。
 * キューは複数スレッドであるから、キューはスレッドセーフにする必要がある
 * キューが空の場合、consumerはproducerがデータを生成するのを待つ
 * キューが満杯の場合、producerはconsumerがデータを消費するのを待つ

■ 構成

 * producer = データを生産する生産者(スレッドを継承する)
 * consumer = データを消費する消費者(こっちもスレッドを継承する)

■ サンプル

http://www.hyuki.com/dp/dpinfo.html#ProducerConsumer
を参考に、Producer-Consumer パターンを理解するためのプログラムを組む。
(実際には、BlockingQueueを使った方がいいらしいので、あくまで理解のため)

 * producer = Customer(お客さん)
 * consumer = SE(システム・エンジニア)

SampleQueue.java

import java.util.Random;

public class SampleQueue {
    private String targetJobInQueue = null;
    synchronized void order(String job) throws InterruptedException {
        while (this.targetJobInQueue != null) {
            System.out.println(
            		Thread.currentThread().getName() + " waits (order)");
            wait();
        }
        System.out.println(Thread.currentThread().getName() + " order " + job);
        this.targetJobInQueue = job;
        this.doHeavyJob();
        System.out.println(this.targetJobInQueue + " is in the Queue.");
        notifyAll();
    }
    synchronized String work() throws InterruptedException {
        while (targetJobInQueue == null) {
            System.out.println(
            		Thread.currentThread().getName() + " waits (work)");
            wait();
        }
        String job = this.targetJobInQueue;
        System.out.println(Thread.currentThread().getName() + " use " + job);
        this.targetJobInQueue = null;
        this.doHeavyJob();
        System.out.println("There is no job in the Queue.");
        notifyAll();
        return job;
    }
    private void doHeavyJob() throws InterruptedException {
    	Random random = new Random();
        Thread.sleep(random.nextInt(5000));
    }
}

SampleProducer.java

public class SampleProducer extends Thread {
    private int counter = 0;
    private SampleQueue queue;
    private String job;
    
    public SampleProducer(String job, SampleQueue queue) {
        this.queue = queue;
        this.job = job;
    }
    public void run() {
        try {
            while (true) {
            	this.queue.order(job + " No." + this.counter);
            	this.counter++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

SampleProducer.java

public class SampleConsumer extends Thread {
	private SampleQueue queue;
    public SampleConsumer(SampleQueue queue) {
        this.queue = queue;
    }
    public void run() {
        try {
            while (true) {
            	this.queue.work();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

SampleProducerConsumerMain.java

public class SampleProducerConsumerMain {
    public static void main(String[] args) {
    	SampleQueue queue = new SampleQueue();
        new SampleProducer("System Designing", queue).start();
        new SampleProducer("Programing", queue).start();
        new SampleProducer("System Test", queue).start();
        new SampleConsumer(queue).start();
        new SampleConsumer(queue).start();
        new SampleConsumer(queue).start();
    }
}


関連記事

デザインパターン / マルチスレッド の分類 ~目次~

https://blogs.yahoo.co.jp/dk521123/34195603.html

Active Object パターン

https://blogs.yahoo.co.jp/dk521123/23819359.html

Java】コレクション ~ Queue 編~

https://blogs.yahoo.co.jp/dk521123/33006366.html

C#】BlockingCollection ~ Producer-Consumerパターン ~

https://blogs.yahoo.co.jp/dk521123/37943991.html