コンセプト
ブロッキングキューとは、ブロッキング機能を備えたキューです。
特性
キューがいっぱいの場合、キューに入り続けると、他のスレッドがキューから要素を取得するまでブロックされます。
キューが空のときにキューから離れ続けると、他のスレッドがキューに要素を追加するまでブロックされます。
特徴
ブロッキング キューには、デカップリング、ピークシェービング、バレー フィリングという主な特徴があります。
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 が解除されるため、ブロック待ちかどうかは必ずしも真ではありません。キューが満杯か空ではない条件のため、複数の判断が必要です。キューがまだ満杯または空の場合は、再度ブロッキング待機に入ります。