ブロッキングキュー (シミュレートされた実装)

コンセプト

        ブロッキングキューとは、ブロッキング機能を備えたキューです。

特性

        キューがいっぱいの場合、キューに入り続けると、他のスレッドがキューから要素を取得するまでブロックされます。

        キューが空のときにキューから離れ続けると、他のスレッドがキューに要素を追加するまでブロックされます。

特徴

        ブロッキング キューには、デカップリング、ピークシェービング、バレー フィリングという主な特徴があります。

        1. デカップリング

                2 つのサーバー A、B、サーバー A はユーザー リクエストをサーバー B に送信する責任を負い、サーバー B はユーザー リクエストを解決する責任を負います。ただし、2 つのサーバーは直接接続されていません。2 つのサーバー間にはブロッキング キューがあり、サーバー Aはリクエストをブロッキング キューに送信し、サーバー B はブロッキング キューからリクエストを取得します。これにより、2 つのサーバー間に直接接続がなくなり、結合が軽減されます。

                結合を減らすことで、あるサーバーでのエラーが別のサーバーでエラーを引き起こすという状況を防ぎます。

        2. 山を削り、谷を埋める

                また、2 つのサーバー A と B があります。サーバー A は、ユーザー リクエストをサーバー B に送信する役割を担っています。2 つのサーバー間にはブロッキング キューがあります。何らかの特別な状況が発生すると、サーバー A は大量のデータをキューに送信します。キューがいっぱいになってからデータを追加すると、ブロック待ちが発生するため、サーバー B のデータ処理のリズムには影響しませんが、ブロックキューがない場合、サーバー A とサーバー B が直接接続されている場合、ブロック待ちが発生します。サーバー A がサーバー B に大量のリクエストを送信すると、サーバー B がクラッシュする可能性が非常に高くなります。

システムが提供するブロッキングキューの使用

BlockingQueue<Integer> queue=new ArrayBlockingQueue(); //底层是顺序表
BlockingQueue<Integer> queue=new LinkedBlockingQueue();  //底层是链表
BlockingQueue<Integer> queue=new PriorityBlockingQueue();  //底层是优先级队列(堆)

        put メソッドを呼び出すとキューにデータが追加され、take メソッドを呼び出すとキューにデータがポップされます。BlockingQueue は Queue インターフェイスを実装しているため、offer や Paul などのメソッドもありますが、put メソッドと take メソッドのみです。ブロックされているため、put メソッドと take メソッドを使用することをお勧めします

コード表示

class MyBlockingQueue{
    //用循环顺序表来作为阻塞队列的底层实现
    private String[]items=new String[1000];
    //数据存在的范围是[head,tail)
    //避免出现内存可见性和指令重排序的问题,要对变量加上volatile
    //指向头部的指针
    private volatile int head=0;
    //指向尾部的指针
    private volatile int tail=0;
    //记录阻塞队列中的数据个数
    private volatile int size=0;


    //put和take方法在多线程中涉及到多个线程改变同一个变量,所以要加上锁
    //向阻塞队列插入数据
    public void put(String elem) throws InterruptedException {
        synchronized(this){
            //判断队列是否满了
            while (size>=items.length){ //不一定阻塞等待是被notify正常唤醒的,也就不一定队列真的不满了,所以要用while循环多次判断
                                        //直到真的队列没满才进行接下来的操作(wait进行阻塞等待推荐和while一起使用,多次判断是否满足唤醒条件)
                this.wait();    //当队列满了就进入阻塞等待
            }
            items[tail]=elem;
            tail++;
            size++;
            if(tail>=items.length){
                tail=0;
            }
            this.notify();  //当向阻塞队列插入数据后,便可以唤醒弹出数据的阻塞状态
        }
    }

    //弹出阻塞队列中的数据
    public String take() throws InterruptedException {
        synchronized(this){
            //判断阻塞队列是否为空
            while (size==0){//不一定阻塞等待是被notify正常唤醒的,也就不一定队列真的不为空,所以要用while循环多次判断
                        //直到真的队列不为空才进行接下来的操作(wait进行阻塞等待推荐和while一起使用,多次判断是否满足唤醒条件)
                this.wait();
            }
            String elem=items[head];
            head++;
            size--;
            if(head>=items.length){
                head=0;
            }

            this.notify();  //弹出一个数据后队列不满了就可以唤醒插入数据的阻塞状态
            return elem;
        }
    }

}

        コード分​​析

                1. このコードのブロッキング キューの基礎となる構造は、非常に単純な循環配列です。

                2. マルチスレッド プログラミングが必要であり、put 操作と take 操作には同じ変数を変更する複数のスレッドが含まれるため、put メソッドと take メソッドをロックするために synchronized を使用する必要があります。また、メモリの可視性や命令の並べ替えの問題を防ぐために、コンパイラーによる最適化を防ぐために、マルチスレッドで読み取りおよび変更する必要がある変数に volatile キーワードを追加します。

同期化およびメモリの可視性と命令の並べ替えについては、「スレッド セーフティの問題」、「スレッド セーフティの問題 (メモリの可視性)」「スレッド セーフティの問題 (命令の並べ替え)」を参照してください。

                3. ブロッキングを実現するにはどうすればよいですか? putメソッドでは、キューが満杯の場合はブロッキング待ちに入り、同期ロックオブジェクトを使用してwaitメソッドを呼び出してブロッキング待ちに入り、takeメソッドが正常に実行された場合のみ、notifyを呼び出してウェイクアップできます。 put メソッドでブロックされた待機中のスレッド。

                また、take メソッドでは、キューが空の場合はブロッキング待ちに入り、同期ロックされたオブジェクトを使用して wait メソッドを呼び出してブロッキング待ちに入り、put メソッドが正常に実行された場合のみ、notify を呼び出してウェイクアップできます。 take メソッドでブロックされた待機中のスレッドを起動します。waitとnotifyに関しては、 waitとnotifyによってスレッドの実行順序が調整されていることがわかります。

                4. put メソッドと take メソッドは同じオブジェクトをロックする必要があります。put メソッドと take メソッドで変更される変数が同じであるため、複数のスレッドが put メソッドと take メソッドを呼び出すとスレッドの安全性の問題が発生するため、同じオブジェクトをロックする必要があります。ロックしました ロックします。

                5. ブロック待ちに入るかどうかの判定条件は while ループ(while と wait() を併用することを推奨)、必ずしもブロック待ちになるわけではないため、通常、notify で wait が解除されるため、ブロック待ちかどうかは必ずしも真ではありません。キューが満杯か空ではない条件のため、複数の判断が必要です。キューがまだ満杯または空の場合は、再度ブロッキング待機に入ります。

おすすめ

転載: blog.csdn.net/q322359/article/details/131998041