生産者消費者問題、Java実現


さあ、今日はこの問題を解決してみましょう.やはりこのようなリズムで、1つの問題を見てまず歴史から見て、全局から見て、このように私たちは本当にその全貌を掌握することができて、最終的にそれぞれ撃破して、胸にはっきりしています!
まず、次の概念を復習します.
1.基礎概念
きほん
  • プログラム-プログラムは静的なソースコードまたはターゲットプログラムであり、生命のないエンティティである.
  • プロセス-Process CPUがプログラムに生命を与えたとき、すなわちオペレーティングシステムがそれを実行すると、プログラムはプロセス-実行中のプログラムと呼ばれるアクティブなエンティティ(実行可能なエンティティではない)となる.
  • プロセスはプログラムの一例である.
  • はコンピュータが資源を分配する基本単位である.
  • はスレッドのコンテナである.

  • スレッド-Threadスレッドはプロセスの実行可能なエンティティであり、システムの独立したスケジューリングと基本単位であり、スレッド自体はシステムリソースを持っていない.
  • スレッドは現代のオペレーティングシステムの重要な指標である.
  • プロセス内のすべてのスレッドは、プロセスのすべてのリソースを共有します.


  • ステップアップ
    1.プロセス間通信-IPC
    プロセス間通信-Inter-Process Communication,IPC;IPCは標準的なUNIX通信メカニズムであり、プログラマが異なるプロセスを調整し、1つのOSで同時に動作させ、互いに情報を伝達し、交換することができるプログラミングインターフェースのセットである.
    最初のIPC方法は2つあります.
  • 信号−Signalsは、UNIXシステムにおいて最も早く使用されるIPC方法である.OSには、キーボードの割り込みやエラーなどの一連のイベント(信号源)が予め定められており、これらのイベントは信号を生成することができる.OSは、現在のプロセスシステムで予め定められたイベントが発生したことを信号によって通知する.プロセスが信号の到来を認識すると、適切な動作を取って信号を送信または処理する.
  • .
  • 配管-PIPE保留表.

  • その後、System V UNIX(1983、正統UNIX)では3つのIPCメカニズムが初めて導入された.
  • メッセージキュー-message queuesメッセージキューは、1つ以上のプロセスがメッセージを書き込み、1つ以上のプロセスがメッセージを読み出すことを可能にするメッセージのチェーンテーブルです.Linuxは、msgqueがメッセージキューを表すMQベクトルテーブルを維持します.
  • 信号量/旗語-semaphore信号量は本質的に、あるリソースのアクセス状況を記録するためのカウンタである.反発信号量、条件信号量があります.
  • 共有メモリ-shared memory共有メモリは通常、1つのプロセスによって作成され、残りのプロセスはこのメモリを読み書きします.通常、共有メモリは信号量で管理されます.

  • もう一つ重要な方法は
  • Socket-ソケットTCP/IPはネットワーク通信プロトコルであり、Socketはこれらのプロトコルの実装APIであり、BSD UNIXで最も早く実装された.今日Socketは最も一般的なネットワークプログラミングAPIであり、TCP/IPプロトコルスタックを提供するすべてのOSにSocketAPIが提供されています.

  • 2.スレッド間の通信方法
    プロセスは以上のように多くのIPC手段で通信することができますが、スレッドは?
    実はスレッド間の通信は簡単です.同じプロセスのスレッドたちはプロセスのリソースを共有しているので、それらの間の通信は実は共有リソースを読み書きします.もちろん、データの一貫性を保証するために反発手段が必要になる可能性があります.
    tips:
    Javaスレッドの同期に関する知識点が分かると思います.スレッドの同期時に使用される「共有リソース」とは、クラス内のメンバー変数のようなもので、実際にはJVM実行時のメモリ内のスタックまたはメソッド領域にあり、この2つの領域はスレッド共有されています.さらに説明すると、この領域はJVMプロセス(Process)のリソース領域、つまりすべてのJVMスレッド(Thread)が読み書き可能な共有リソースである.
    スレッド同期は、スレッドが共有リソースに安全にアクセスすることを保証する手段であり、同期方法は以下のとおりである.
  • イベント-Event
  • 反発-Mutex
  • 信号量/旗語-Semaphore
  • 臨界資源
  • 2.生産者消費者問題
    1.問題を投げ出す
    生産者消費者問題(Producer-consumer problem)は、有限バッファ問題(Bounded-buffer problem)とも呼ばれます.この問題は古典的な(簡単な)プロセス/スレッド同期問題です.
    問題は次のとおりです.
    まず、工場には製品バッファがあり、生産者はバッファに新製品を追加し続け、消費者はバッファから製品を取り出す.
    問題:生産者がバッファがいっぱいになったときにデータを入れないことをどのように保証し、消費者がバッファが空になったときにデータを消費しないことを保証しますか?
    答えも簡単です.
    生産者は、消費者が製品を消費するまでバッファがいっぱいになると休眠しなければならない.同時に、生産者がバッファに製品を追加するまで、消費者を呼び覚ますことができないように、消費者をバッファ空で休眠させなければならない.
    プログラム設計の面では、ソリューションは上記のIPC方法であり、その中で最もよく使われるのは信号量方法であるため、信号量について具体的に説明します.
    2.信号量-semaphore
    1.概説上述したように、信号量は本質的にカウンタであり、プロセス同期の問題を処理するために使用することができる.
    1965年、オランダのコンピューター科学者エズガー・ディジェストラ(Edsger W.Dijkstra)がSemaphoreメカニズムを発明し、現在は様々なOSに広く応用されている.
    2.OSの記述:システムでは、各プロセスに現在の状態を表す信号量が与えられ、制御権が得られていないプロセスは特定の場所で強制的に停止され、継続可能な信号の到来を待つ.
    3.種類:
  • 信号量は、カウント信号量(Counting semaphore)または一般信号量(general semaphore)
  • と呼ばれる任意の整数である.
  • 信号量がバイナリのみの0または1をバイナリ信号量(Binary semaphore)または反発信号量-Mutexと呼ぶ.
  • 4.PV原語:Dijkstraは同時に2つの原語(原子文)を提案してsemaphoreを操作する:
  • P原語Pはオランダ語Proeren(テスト)の頭文字である.Pはブロック原語であり、現在のプロセスを実行状態からブロック状態に変換し、別のプロセスが起動するまで担当する.代表的な操作は、空きリソース(信号量-1)を申請し、成功すれば終了する.失敗すれば、そのプロセスはブロックされる.
  • V原語Vはオランダ語Verhogen(増加)の頭文字である.Vは原語を呼び覚ますため、ブロックされたプロセスを呼び覚ます責任を負う.パラメータテーブルがあり、呼び覚ますのを待つプロセスを記録している.代表的な操作は、占有されたリソース(信号量+1)を解放し、ブロックされたスレッドが発見された場合、呼び覚ますことを選択する.
  • 5.3種類の使用:3の2種類と4のPV原語を組み合わせて、semaphoreの操作を3種類に分けることができます:
  • semaphoreをロックフラグビットと見なし、共有変数への反発アクセスを実現する.プロセス:
    P(mutex); // mutex     1,       ;
    V(mutex);
    //    
  • semaphoreを共有リソースの残りの数と見なし、1つのクラスの共有リソースへのアクセスを実現する.プロセス:
    P(mutex); // mutex          N,     ;
    V(mutex);
    //    
  • semaphoreはプロセス間の同期ツールとみなされます.プロセス:
       C1;
    P(S); 
    V(S);
       C2;
  • 3.Javaでの実装
    生産者の消費者問題は具体的には以下のようなものがある.
  • 生産者1人消費者
  • 生産者N人消費者
  • M個の生産者N個の消費者
  • ネット上の大部分の人が書いたのはすべて教育の性質の第1種だと感じて、以下私自身は私の理解によって1つの第3種を書いて、実は私も正しいかどうか分かりません......私が正しいと思って、参照することができることを見つけていないためです......
    読者の皆様、ご不適切な点があれば、ご指導を惜しまないでください.
    import java.util.LinkedList;
    import java.util.Random;
    
    /** *     、        * * @author alanzhangyx * */
    public class ProducerConsumer {
    
        //         ,   Integer
        LinkedList<Integer> list = new LinkedList<Integer>();
    
        //         
        static final int MAX_SIZE = 100;
    
        /** *    。 * * <p>     V    </p> * <ul> * <li>     ,         MAX_SIZE,       (n   )     ,       </li> * <li>           (   wait)</li> * </ul> * * @version 1.0.0 * @author alanzhangyx */
        class Producer implements Runnable{
            @Override
            public void run() {
                while (true) {
                    synchronized (list){
                        if (list.size() < MAX_SIZE) {
                            int num = new Random().nextInt(100);
                            list.add(num);
                            list.notifyAll();
                            System.out.println("   " + Thread.currentThread().getName() + "     :" + num + ",----        " + list.size());
    
                        }
    
                        try {
                            list.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        /** *    。 * * <p>     P    </p> * <ul> * <li>     ,        ,           (n   ),       </li> * <li>           (   wait)</li> * </ul> * * @version 1.0.0 * @author alanzhangyx */
        class Consumer implements Runnable{
            @Override
            public void run() {
                while (true) {
                    synchronized (list){
                        if (list.size() > 0) {
                            int num = list.poll();//poll Queue   ,       
                            System.out.println("   " + Thread.currentThread().getName() + "     :" + num + ",----        " + list.size());
                            list.notifyAll();
                        }
    
                        try {
                            list.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
    
        public static void main(String[] args) {
            ProducerConsumer pc = new ProducerConsumer();
    
            //Thread        Runnable            ,Runnable        ,  new  
            //     ,     
            Consumer c = pc.new Consumer();
            Producer p = pc.new Producer();
    
            //         start   
            new Thread(c).start();
            new Thread(c).start();
            new Thread(c).start();
            new Thread(c).start();
            new Thread(c).start();
    
            new Thread(p).start();
            new Thread(p).start();
            new Thread(p).start();
            new Thread(p).start();
            new Thread(p).start();
    
        }
    
    }
    

    締めくくり
    皆さんは分かりましたか?実は私がまとめたこれらはただはっきりしたことを書いただけで、いつもそんなに一貫していない感じがして、実は一部の地方が私がまだあまり徹底的に理解していないためです.
    ネット上の関連問題の説明もぼんやりしていて、困っています.
    いずれにしても、いくつかのものはしばらくしてから戻ってから新しい考えを見つけることができて、しばらくこのようにして、後で新しい理解があればすぐに補充します.