回答:
プロデューサー/コンシューマースレッドの場合、ConcurrentLinkedQueue
それが妥当なオプションであるかどうかはわかりません。実装されていませんBlockingQueue
。これは、プロデューサー/コンシューマーキューIMOの基本的なインターフェイスです。を呼び出しpoll()
、何も見つからなかった場合は少し待ってから、もう一度ポーリングする必要があります...新しいアイテムが入ってくると遅延が発生し、空の場合は非効率になります(スリープから不必要に目覚めるため)。 。
BlockingQueueのドキュメントから:
BlockingQueue
実装は、主にプロデューサー-コンシューマーキューに使用するように設計されています
私は、プロデューサー/コンシューマーキューにブロッキングキューのみを使用する必要があるとは厳密に言っていませんが、それでも...
ConcurrentLinkedQueue
は、スレッドが複数のキューをチェックしている場合にも役立ちます。たとえば、マルチテナントサーバーです。分離の理由で、代わりに単一のブロッキングキューとテナント識別子を使用しないと仮定します。
take()
とput()
単によりも余分なリソース(同期のinterms)を消費しますConcurrentLinkedQueue
。のための有界キューを使用する場合であるが、生産者-消費者のシナリオ
この質問はより良い答えに値します。
Java ConcurrentLinkedQueue
は、Maged M. MichaelとMichael L. Scottによる、ブロッキングのないロックフリーキュー用の有名なアルゴリズムに基づいています。
ここで競合するリソース(キュー)の用語としての「非ブロッキング」とは、スレッドの中断などのプラットフォームのスケジューラの動作に関係なく、または問題のスレッドが単に遅すぎる場合、他のスレッドが同じリソースについて競合していることを意味しますまだ進行できます。たとえば、ロックが関係している場合、ロックを保持しているスレッドが中断され、そのロックを待機しているすべてのスレッドがブロックされる可能性があります。synchronized
Javaの組み込みロック(キーワード)も、バイアスされたロックのように、パフォーマンスに重大なペナルティをもたらす可能性がありますが関与していて、競合が発生した場合、またはVMがスピン猶予期間後にロックを「膨らませ」、競合するスレッドをブロックすることを決定した後...多くのコンテキスト(低/中競合のシナリオ)で比較し、アトミック参照の-setsははるかに効率的であり、これはまさに多くの非ブロッキングデータ構造が行っていることです。
Java ConcurrentLinkedQueue
は非ブロッキングであるだけでなく、プロデューサーがコンシューマーと競合しないという素晴らしい特性を持っています。単一のプロデューサー/シングルコンシューマーシナリオ(SPSC)では、これは実際に話す必要のある競合がないことを意味します。複数のプロデューサー/シングルコンシューマーのシナリオでは、コンシューマーはプロデューサーと競合しません。このキューは、複数のプロデューサーがを試みたときに競合しますが、それは本質的にoffer()
同時実行性です。基本的には、汎用で効率的な非ブロッキングキューです。
それがでないことに関してはBlockingQueue
、スレッドをキューで待機するのをブロックすることは、並行システムを設計するためにひどく恐ろしい方法です。しないでください。ConcurrentLinkedQueue
コンシューマー/プロデューサーのシナリオでを使用する方法がわからない場合は、優れたアクターフレームワークのように、より高いレベルの抽象化に切り替えるだけです。
LinkedBlockingQueue
キューが空または一杯で、それぞれのコンシューマ/プロデューサスレッドがスリープ状態になると、コンシューマまたはプロデューサをブロックします。ただし、このブロッキング機能にはコストが伴います。すべてのputまたはtake操作は、プロデューサーまたはコンシューマー(存在する場合)の間で競合するため、多くのプロデューサー/コンシューマーが存在するシナリオでは、操作が遅くなる可能性があります。
ConcurrentLinkedQueue
はロックを使用していませんが、そのput / take操作でCASを使用しているため、多くのプロデューサスレッドとコンシューマスレッドとの競合が減少する可能性があります。ただし、「待機なし」のデータ構造であるConcurrentLinkedQueue
ため、空の場合はブロックされません。つまり、コンシューマースレッドはCPUを使い果たすなど、「ビジー待機」によってtake()
戻りnull
値を処理する必要があります。
したがって、どちらが「より良い」かは、コンシューマスレッドの数、それらが消費/生成する速度などに依存します。各シナリオにはベンチマークが必要です。
ConcurrentLinkedQueue
明らかに優れている1つの特定の使用例は、プロデューサーが最初に何かを作成し、キューに作業を配置して、コンシューマーが消費を開始した後にのみ、ジョブが終了するときです。(ここでは、プロデューサーとコンシューマーの間には並行性はありませんが、プロデューサーとプロデューサーとコンシューマーの間には並行性があります)
unbounded blockingqueue
使用する場合はCASベースのコンカレントよりも優れていますConcurrentLinkedQueue