設計モデル--生産者消費者モデル


一般的なシーン:
あるモジュールは、別のモジュールが処理するデータの生成を担当します.データを生成するモジュールを、イメージ的に生産者と呼ぶ.データを処理するモジュールを消費者と呼ぶ.
このモデルはまた、仲介者として生産者と消費者の間にバッファを置く必要がある.生産者はデータをバッファに入れ、消費者はバッファからデータを取り出す.
バッファ作用
  • はデカップリングされ、生産者と消費者はバッファにのみ依存し、
  • には互いに依存しない.
  • は、同時および非同期
  • をサポートします.
    食品加工工場があると仮定すると、一定量の食品を保管できる倉庫があり、工場は絶えず食品を生産し、倉庫に保管し、消費者は絶えず倉庫から食品を購入している.
    次に、配列シミュレーションスタックを用いて設計モードを実現する
    1.エンティティークラスの作成
    public class Food {
        private int id;
    
        Food(int id){
            this.id = id;
        }
    
        @Override
        public String toString(){
            return "  【" + id + "】   ";
        }
    }

    2.倉庫クラスの作成
    public class FoodStoreHouse {
        private int index = 0;
    //         
        public Food[] foodBuffer  = new Food[5];
    
        public synchronized void push(Food food){
            try{
                while(index == foodBuffer.length){
                    this.wait();
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            foodBuffer[index++] = food;
            this.notifyAll();
        }
    
        public synchronized Food pop(){
            try{
    //              while    if,         catch        
                while(index == 0){
                    this.wait();
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            Food food = foodBuffer[--index];
            this.notifyAll();
            return food;
        }
    }

    3.生産者の作成
    public class Productor implements Runnable {
    
        private FoodStoreHouse foodHouse;
    
        Productor(FoodStoreHouse foodHouse){
            this.foodHouse = foodHouse;
        }
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try{
                product();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    
        private void product() throws InterruptedException{
            for(int i=0; i<5; i++){
                Food food = new Food(i);
                System.out.println(Thread.currentThread().getName() + "    " + food.toString());
                foodHouse.push(food);
                Thread.sleep((int) (Math.random()*500));
            }
        }
    
    }

    4.消費者の作成
    public class Comsumer implements Runnable{
        private FoodStoreHouse foodHouse;
    
        Comsumer(FoodStoreHouse foodHouse){
            this.foodHouse = foodHouse;
        }
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try{
                comsume();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
    
        }
    
        private void comsume() throws InterruptedException{
            for(int i=0; i<5; i++){
                Food food = foodHouse.pop();
                System.out.println(Thread.currentThread().getName() +  "---    " + food.toString());
                Thread.sleep((int) (Math.random()*1000));
            }
        }
    
    }

    5.テストクラス
    public class TestComsumerProductor {
    
        public static void main(String[] args){
            FoodStoreHouse foodHouse = new FoodStoreHouse();
            Productor p = new Productor(foodHouse);
            Comsumer c = new Comsumer(foodHouse);
    
            Thread p1 = new Thread(p);
            p1.setName("1    ");
            Thread p2 = new Thread(p);
            p2.setName("2    ");
            Thread c1 = new Thread(c);
            c1.setName("1    ");
            Thread c2 = new Thread(c);
            c2.setName("2    ");
    
    
            p1.start();
            p2.start();
            c1.start();
            c2.start();
        }
    }

    6.テスト結果
    1          【0】   
    2          【0】   
    1    ---      【0】   
    2    ---      【0】   
    2          【1】   
    1          【1】   
    2          【2】   
    2          【3】   
    2          【4】   
    1          【2】   
    2    ---      【4】   
    1    ---      【2】   
    1          【3】   
    1    ---      【3】   
    2    ---      【3】   
    1          【4】   
    1    ---      【4】   
    2    ---      【2】   
    1    ---      【1】   
    2    ---      【1】   

    次にBlockingQueueを用いてこのモードを実現する
    BlockingQueueはブロックキューであり、ブロックという言葉から、ブロックキューへのアクセスがブロックされる場合があることがわかります.ブロックされる場合は主に次の2つがあります.
  • キューがいっぱいになったときにエンキュー動作
  • を行う.
  • キューが空であるときにデキュー動作
  • を行う.
    したがって、1つのスレッドが満たされたキューに対してキュー操作をしようとすると、別のスレッドがキュー操作を行わない限り、ブロックされます.同様に、1つのスレッドが空のキューをアウトキュー操作しようとすると、別のスレッドが入キュー操作を行わない限り、ブロックされます.
    Javaでは、BlockingQueueのインタフェースはjava.util.concurrentパッケージ(Java 5バージョンから提供される)にあり、前述したブロックキューの特性から、ブロックキューはスレッドが安全であることがわかります.
    BlockingQueueのコアメソッド:
    データを挿入:
    offer(anObject):可能であれば、anObjectをBlockingQueueに追加することを示す.すなわち、BlockingQueueが収容可能であればtrueを返し、そうでなければfalse.(本方法では現在の実行方法のスレッドをブロックしない)offer(E o,long timeout,TimeUnit unit):待機時間を設定でき、指定した時間内に、キューにBlockingQueueを追加できない場合は、エラーが返されます.put(anObject):anObjectをBlockingQueueに追加し、BlockQueueにスペースがない場合、このメソッドを呼び出すスレッドはBlockingQueueの中にスペースがあるまでブロックされます.  
    データの取得:
    Poll(time):BlockingQueueで1位のオブジェクトを取り出し、すぐに取り出せない場合はtimeパラメータで規定された時間を待つことができ、取り出せない場合はnullに戻ることができます.Poll(long timeout,TimeUnit unit):BlockingQueueからキューヘッダのオブジェクトを取り出し、指定した時間内にキューにデータがあれば、すぐにキュー内のデータを返します.タイムアウトしてもデータが取得できない場合は、エラーを返します.take():BlockingQueueで1位のオブジェクトを取り出し、BlockingQueueが空の場合、BlockingQueueに新しいデータが追加されるまでスレッドをブロックします.drainTo():BlockingQueueから利用可能なすべてのデータオブジェクトを一度に取得し(取得データの個数を指定することもできる)、この方法により、取得データの効率を向上させることができる.複数のバッチロックやロックの解除は必要ありません.
    BlockingQueueを使用するメリット:
    マルチスレッド環境では、プログラマー一人一人がこれらの詳細を自分で制御する必要があります.特に、効率とスレッドセキュリティを両立させなければなりません.これは、プログラムに大きな複雑さをもたらします.BolckingQueueを使用すると、スレッドをブロックする必要があるとき、スレッドを起動する必要があるときに関心を持つ必要はありません.
    *実装コード*1.エンティティークラスの作成
    public class Food {
        private int id;
    
        Food(int id){
            this.id = id;
        }
    
        @Override
        public String toString(){
            return "  【" + id + "】   ";
        }
    }

    2.生産者の作成
    import java.util.concurrent.BlockingQueue;
    
    public class Productor implements Runnable {
    
        private BlockingQueue foodHouse;
    
        Productor(BlockingQueue foodHouse){
            this.foodHouse = foodHouse;
        }
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try{
                product();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    
        private void product() throws InterruptedException{
            for(int i=0; i<5; i++){
                Food food = new Food(i);
                System.out.println(Thread.currentThread().getName() + "    " + food.toString());
                foodHouse.put(food);
                Thread.sleep((int) (Math.random()*500));
            }
        }
    
    }

    3.消費者の作成
    import java.util.concurrent.BlockingQueue;
    
    public class Comsumer implements Runnable{
        private BlockingQueue foodHouse;
    
        Comsumer(BlockingQueue foodHouse){
            this.foodHouse = foodHouse;
        }
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try{
                comsume();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
    
        }
    
        private void comsume() throws InterruptedException{
            for(int i=0; i<5; i++){
                Food food = foodHouse.take();
                System.out.println(Thread.currentThread().getName() +  "---    " + food.toString());
                Thread.sleep((int) (Math.random()*1000));
            }
        }
    
    }

    4.テストクラス
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    public class TestComsumerProductor {
    
        public static void main(String[] args){
        //      LinkedBlockingQueue   BlockingQueue,            
            BlockingQueue foodHouse = new LinkedBlockingQueue();
            Productor p = new Productor(foodHouse);
            Comsumer c = new Comsumer(foodHouse);
    
            Thread p1 = new Thread(p);
            p1.setName("1    ");
            Thread p2 = new Thread(p);
            p2.setName("2    ");
            Thread c1 = new Thread(c);
            c1.setName("1    ");
            Thread c2 = new Thread(c);
            c2.setName("2    ");
    
    
            p1.start();
            p2.start();
            c1.start();
            c2.start();
        }
    }

    5.テスト結果
    2          【0】   
    1          【0】   
    2    ---      【0】   
    1    ---      【0】   
    1          【1】   
    2          【1】   
    1    ---      【1】   
    2          【2】   
    1          【2】   
    1          【3】   
    1    ---      【1】   
    2          【3】   
    2          【4】   
    2    ---      【2】   
    1          【4】   
    1    ---      【2】   
    2    ---      【3】   
    2    ---      【3】   
    1    ---      【4】   
    2    ---      【4】