キューは、「先入れ先出し」の原則に従う特別な線形テーブルです。私たちの日常的な使用では、データを同時に操作するためによく使用されます。並行プログラミングでは、スレッドセーフなキューの使用が必要になる場合があります。スレッドセーフ キューを実装する場合は、通常 2 つの方法があります。1 つはブロッキング キューを使用する方法、もう 1 つはスレッド同期ロックを使用する方法です。
ブロッキング キューとは何ですか?
パン屋があり、客がパンを食べ、シェフがパンを焼いているとします。かごの中のパンは2個までで、テストが終わったらシェフがパンをかごに入れ、ゲストがパンを食べるときはかごからパンを取り出します。ゲストがパンを食べるときにカゴの中にパンがあるか、シェフがパンを焼くときにカゴが溢れていないのか、このとき、ブロッキングキューの概念を導入する必要があります。これは、私たちがよく生産者-消費者モデルと呼ぶものです。
ブロッキング キューは、2 つの追加操作をサポートするキューです。これら 2 つの追加操作は、挿入および削除メソッドのブロックをサポートします。
(1) ブロッキング挿入メソッドのサポート: これは、キューがいっぱいになると、キューがいっぱいになるまで要素を挿入するスレッドをブロックすることを意味します。
(2) ブロッキング削除メソッドのサポート: これは、キューが空の場合、要素を取得するスレッドはキューが空でなくなるまで待機することを意味します。ブロッキング キューは、プロデューサーおよびコンシューマーのシナリオでよく使用されます。プロデューサーはキューに要素を追加するスレッドであり、コンシューマーはキューから要素を取得するスレッドです。ブロッキング キューは、プロデューサーが要素を保存するために使用し、コンシューマーが要素を取得するために使用するコンテナです。
システム内のノンブロッキング キュー: PriorityQueue と ConcurrentLinkedQueue
ノンブロッキング キュー間の関係を見てみましょう (PriorityQueue を例にします):
PriorityQueue クラスは AbstractQueue を継承し、Serializable インターフェイスを実装します。基本的に順序付きリストを維持する PriorityQueue は Java util パッケージにあります。名前の前半の Priority という単語は優先順位を意味します。実際、このキューには「優先順位」があります。キューに追加された要素は、自然な順序に従って (java.util.Comparable 実装を通じて)、またはコンストラクターに渡された java.util.Comparator 実装に従って配置されます。
ConcurrentLinkedQueue は、リンクされたノードに基づくスレッドセーフなキューです。同時アクセスには同期は必要ありません。 ConcurrentLinkedQueue はキューの末尾に要素を追加し、先頭から要素を削除するため、キューのサイズがわからなくても、共通コレクションへの ConcurrentLinkedQueue の共有アクセスは問題なく機能します。キュー サイズに関する情報の収集には時間がかかり、キューをトラバースする必要があります。ConcurrentLinkedQueue は、リンクされたノードに基づく無制限のスレッドセーフ キューです。先入れ先出しルールを使用してノードを並べ替えます。要素を追加すると、キューの最後尾に追加されます。要素を取得すると、キューの先頭の要素が返されます。
ブロッキング インターフェイスを実装するキュー:
BlockingQueue インターフェイスと 5 つのブロッキング キュー クラスが java.util.concurrent に追加されます。これは本質的には FIFO データ構造にひねりが加えられたものです。キューに要素をすぐに追加または削除するのではなく、スペースまたは要素が使用可能になるまで操作を実行するスレッドはブロックされます。
5 つのキューは、それぞれ異なるものを提供します。
·ArrayBlockingQueue: 配列によってサポートされる境界付きキュー。
·LinkedBlockingQueue: リンクされたノードによってバックアップされるオプションの制限付きキュー。
·PriorityBlockingQueue: 優先度ヒープを基盤とする無制限の優先度キュー。
·DelayQueue: 優先度ヒープに基づく時間ベースのスケジューリング キュー。
·SynchronousQueue: BlockingQueue インターフェイスを使用した単純なランデブー メカニズム。
ArrayBlockingQueue と LinkedBlockingQueue の間の継承関係を見てみましょう:
次の間の継承関係を見てみましょう。この 2 つのクラスは、AbstractQueue から継承し、Serializable インターフェイスも実装していることがわかりますが、違いは、BlockingQueue インターフェイスも実装していることです。
いくつかを簡単に紹介しましょう:
LinkedBlockingQueueLinkedBlockingQueue のデフォルト サイズは Integer.MAX_VALUE で、キャッシュされた境界付き待機キューとして理解できます。最大容量を指定することを選択できます。リンク リストに基づくキューです。このキューは、FIFO に従って要素を並べ替えます。 (先入先出)。プロデューサがデータをキューに入れると、そのデータはキュー内にキャッシュされます。キュー バッファが最大キャッシュ容量に達すると (LinkedBlockingQueue はコンストラクタを通じてこの値を指定できます)、コンシューマがデータを消費するまで、プロデューサ キューはブロックされます。データの一部がドロップされると、プロデューサ スレッドが起動され、コンシューマではその逆が行われます。
ArrayBlockingQueue は構築時に容量を指定する必要があり、公平性が必要かどうかを選択できます。公平性パラメータが true に設定されている場合、待ち時間が最も長いスレッドが最初に処理されます (実際には、ReentrantLock を に設定することで実現されます)。 true この種の公平性: つまり、待機時間が最も長いスレッドが最初に動作します)。一般に、公平性を高めるとパフォーマンスが低下するため、本当に必要な場合にのみ使用してください。これは、FIFO (先入れ先出し) 原則に従って要素を並べ替える、配列ベースのブロッキング循環キューです。
PriorityBlockingQueue は優先キューであり、先入れ先出しキューではありません。要素は優先度の順に削除され、キューには上限がありません (ソース コードを見ると、PriorityBlockingQueue はヒープ データ構造に基づいて PriorityQueue を再パッケージしたものであり、PriorityQueue には ArrayList と同様に容量制限がありません。ブロッキング キューに入れるときはブロックされません。このキューは論理的には制限されていませんが、リソースが使い果たされているため、追加操作を実行しようとすると OutOfMemoryError が発生する可能性があります)、ただし、キューが空の場合、要素の取得はブロックされるため、その取得操作のテイクはブロックされます。さらに、キューに入る要素には比較機能が必要です。
ConcurrentLinkedQueue と LinkedBlockingQueue について:
は、ブロッキング キューと非ブロッキング キューの違いとしても理解できます:
1.LinkedBlockingQueue は、ロック メカニズムでは、ConcurrentLinkedQueue は CAS アルゴリズムを使用しますが、LinkedBlockingQueue の基礎となるロックの取得にも CAS アルゴリズムが使用されます。
2. 要素の取得に関して、ConcurrentLinkedQueue は要素を取得するためのブロックをサポートしていませんが、LinkedBlockingQueue はブロックする take() メソッドをサポートしています。
3. 要素挿入のパフォーマンスについては、特にマルチ CPU サーバーで実際に使用すると、ロックとロックなしの差が反映され、ConcurrentLinkedQueue は LinkedBlockingQueue よりもはるかに高速になります。
プロデューサー - コンシューマー コード:
プロデューサー - コンシューマーの小さな例をインターネット上で見ましたが、これはブロッキング キューを理解するのに非常に役立ちます。
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class BlockingQueueTest { public static class Basket { BlockingQueue<String> basket = new ArrayBlockingQueue<>(3); private void produce() throws InterruptedException { basket.put("苹果"); } private void consume() throws InterruptedException { basket.take(); } private int getAppleNumber() { return basket.size(); } } private static void testBasket() { final Basket basket = new Basket(); class Producer implements Runnable { public void run() { try { while (true) { System.out.println("生产者开始生产苹果###"); basket.produce(); System.out.println("生产者生产苹果完毕###"); System.out.println("篮子中的苹果数量:" + basket.getAppleNumber() + "个"); Thread.sleep(300); } } catch (InterruptedException e) {} } } class Consumer implements Runnable { public void run() { try { while (true) { System.out.println("消费者开始消费苹果***"); basket.consume(); System.out.println("消费者消费苹果完毕***"); System.out.println("篮子中的苹果数量:" + basket.getAppleNumber() + "个"); Thread.sleep(1000); } } catch (InterruptedException e) {} } } ExecutorService service = Executors.newCachedThreadPool(); Producer producer = new Producer(); Consumer consumer = new Consumer(); service.submit(producer); service.submit(consumer); try { Thread.sleep(10000); } catch (InterruptedException e) {} service.shutdownNow(); } public static void main(String[] args) { BlockingQueueTest.testBasket(); } }
多くの Java トレーニング ビデオ はすべて PHP 中国語 Web サイトにあります。オンラインで学習することを歓迎します。
以上がJavaキューとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。