JavaでProducer-Consumerパターンを実践!

やりたいこと

Javaでマルチスレッドな処理を各必要が出来た。

外部サーバーとやりとりするのだが、自サーバーから外部サーバーへの同時接続は15本に限定されている。逆を言えば15本までは同時接続できるわけだ。

ならば、15本のスレッドを走らせて逐次処理を行っていくのが良い。

スレッドの数が15本と決まっているのならば、プログラムの起動時に、スレッドを15本作って、それを使いまわすのが最も良いだろう。いちいち、スレッドを作ったり破棄したりする手間が減るってものだ。

と、ここまで考えて、そんなデザインパターンなかったかなー。と思って、検索。

デザインパターンなんてGoFデザインパターンですら、記憶が曖昧なので、毎回検索して調べてる。)

マルチスレッドのデザインパターンは数あれど、今回の仕様に当てはまるのは、Producer-Consumerパターンかな。と思った。

Producer-Consumerパターン

簡単に図示すると以下のようになる。(図示ってずじじゃなくて、ずしなんだな。いつもずじずじ言ってたわ)

f:id:omiya6048:20130529133257p:plain

Producerは仕事に必要なデータを集めて、せっせとQueueに詰めていく。ConsumerはQueueからデータを取り出して、処理を行う。

Queueは複数スレッドであるから、Queueはスレッドセーフである必要がある。

と、まぁここまでは簡単に理解できた。あとは実際にサンプルコードを見れば実装可能だ。

というわけで、Javaでのサンプルコード探し。

Javaでのサンプルが掲載された良い記事

すぐにサンプルコードは何個か見つかったが、以下のページが整理されていて、とても分かりやすい。

Producer Consumer Design Pattern with Blocking Queue Example in Java
http://javarevisited.blogspot.jp/2012/02/producer-consumer-design-pattern-with.html

特に、この記事ではProducer-Consumer問題についての解決策について、Javaでの具体的で簡単な解決策が書かれていたのが秀逸。

Producer-Consumer問題(有限バッファ問題)

ConsumerはQueueが空の場合は、Queueにデータが入ってくるまで待機しなくてはいけない。
ProducerはQueueがいっぱいの場合は、Queueが空くまで待機しなくてはいけない。
とまぁ、これが問題らしいんだけど、何が問題なのか恥ずかしながら理解できない。

単純にQueueを各スレッドが監視して、Queueが空の場合は、ProducerがQueueにデータを入れて、Consumerは待機。

Queueが満杯の場合は、Consumerが処理を頑張って、ProducerはQueueが空くまで待機してれば良いんじゃないの?

とりあえず、僕は理解できていないので、詳しいことは英語版Wikipediaの該当記事を見てもらいたい。日本語版Wikipediaでは、セマフォの項で、ちらっと触れられてるレベル。

Producer–consumer problem
http://en.wikipedia.org/wiki/Producer-consumer_problem

セマフォ(生産者/消費者問題
http://ja.wikipedia.org/wiki/%E3%82%BB%E3%83%9E%E3%83%95%E3%82%A9#.E4.BE.8B:_.E7.94.9F.E7.94.A3.E8.80.85.2F.E6.B6.88.E8.B2.BB.E8.80.85.E5.95.8F.E9.A1.8C

日本語でも解説しているページはあるんだけど、どうもピンと来ない。

有限バッファ問題
http://besttseb99.exblog.jp/5512020

うまく同期させないと駄目なのかー。レベルの理解度なんだけど……。もしくは上手く実装しないと無駄なスリープが発生しまくって、実行効率が落ちまくるぜ! って問題なのかなあ。よく分からんね。

でまぁ、先に紹介したJavaのサンプルコードのページでは、色々面倒な実装が必要なんだけど、実はJavaにはBlockingQueueっていうのがあって、これを使えば簡単に実装できるんだぜ!! って書いてある。

実際に、英語版WikipediaにあるJavaのサンプルコードと比較すると、BlockingQueueを使っている方が明らかにコード量が少ない。簡単だ。

問題がよく理解できないまま、僕はBlockingQueueを使えば大丈夫という情報を鵜呑みにすることにした。

とりあえず、Producer-Consumer問題については、宿題とする。

サンプルコード

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ProducerConsumerPattern {

    public static void main(String args[]){
  
     //Creating shared object
     BlockingQueue sharedQueue = new LinkedBlockingQueue();
 
     //Creating Producer and Consumer Thread
     Thread prodThread = new Thread(new Producer(sharedQueue));
     Thread consThread = new Thread(new Consumer(sharedQueue));

     //Starting producer and Consumer thread
     prodThread.start();
     consThread.start();
    }
 
}

//Producer Class in java
class Producer implements Runnable {

    private final BlockingQueue sharedQueue;

    public Producer(BlockingQueue sharedQueue) {
        this.sharedQueue = sharedQueue;
    }

    @Override
    public void run() {
        for(int i=0; i<10; i++){
            try {
                System.out.println("Produced: " + i);
                sharedQueue.put(i);
            } catch (InterruptedException ex) {
                Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

}

//Consumer Class in Java
class Consumer implements Runnable{

    private final BlockingQueue sharedQueue;

    public Consumer (BlockingQueue sharedQueue) {
        this.sharedQueue = sharedQueue;
    }
  
    @Override
    public void run() {
        while(true){
            try {
                System.out.println("Consumed: "+ sharedQueue.take());
            } catch (InterruptedException ex) {
                Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
  
  
}


Read more: http://javarevisited.blogspot.com/2012/02/producer-consumer-design-pattern-with.html#ixzz2Uep1FION