私の質問は、 この質問 以前に尋ねたものに関連しています。プロデューサーとコンシューマースレッド間の通信にキューを使用している状況では、一般的にLinkedBlockingQueue
またはConcurrentLinkedQueue
を使用することをお勧めしますか?
一方を他方より使用することの利点/欠点は何ですか?
APIの観点から見た主な違いは、LinkedBlockingQueue
をオプションで制限できることです。
プロデューサー/コンシューマースレッドの場合、ConcurrentLinkedQueue
が合理的なオプションであることもわかりません。プロデューサー/コンシューマーキューIMOの基本的なインターフェイスであるBlockingQueue
を実装していません。 poll()
を呼び出して、何も見つからなかった場合は少し待ってから、もう一度ポーリングする必要があります...新しいアイテムが入ってくると遅延が発生し、空の場合は非効率になります睡眠から不必要に目覚めるため)。
BlockingQueueのドキュメントから:
BlockingQueue
実装は、主に生産者/消費者キューに使用されるように設計されています
私はそれがstrictlyではないことを知っています。プロデューサー-コンシューマーキューにはブロッキングキューのみを使用する必要があると言いますが、それでも...
この質問はより良い答えに値します。
JavaのConcurrentLinkedQueue
は、有名な Maged M. MichaelとMichael L. Scottのアルゴリズム for non-blocking lock-free キューに基づいています。
競合リソース(キュー)の用語としての「非ブロック」とは、スレッドの割り込みなど、プラットフォームのスケジューラーの動作に関係なく、または問題のスレッドが単に遅すぎる場合、他のスレッドが同じリソースを競合することを意味しますまだ進歩することができます。たとえば、ロックが関係している場合、ロックを保持しているスレッドが中断され、そのロックを待機しているすべてのスレッドがブロックされます。 Javaの本質的なロック(synchronized
キーワード)もパフォーマンスに深刻なペナルティをもたらす可能性があります- バイアスロック が関係している場合など)競合、またはVMがスピン猶予期間後、ロックを「膨張」させ、競合するスレッドをブロックすることを決定した後...多くのコンテキスト(低/中競合のシナリオ)で、アトミック参照の比較と設定ははるかに効率的であり、これはまさに多くのノンブロッキングデータ構造が行っていることです。
JavaのConcurrentLinkedQueue
はノンブロッキングであるだけでなく、プロデューサーがコンシューマーと競合しないという素晴らしい特性を持っています。単一のプロデューサー/単一のコンシューマーシナリオ(SPSC)では、これは実際に言うべき競合がないことを意味します。複数のプロデューサー/単一のコンシューマーのシナリオでは、コンシューマーはプロデューサーと競合しません。複数のプロデューサーがoffer()
を試行する場合、このキューには競合がありますが、それは定義による同時性です。基本的には汎用で効率的なノンブロッキングキューです。
BlockingQueue
でないことに関しては、スレッドをブロックしてキューで待機させることは、並行システムを設計するひどく恐ろしい方法です。しないでください。コンシューマ/プロデューサーシナリオでConcurrentLinkedQueue
を使用する方法がわからない場合は、優れたアクターフレームワークのように、より高レベルの抽象化に切り替えてください。
LinkedBlockingQueue
は、キューが空または満杯で、それぞれのコンシューマ/プロデューサスレッドがスリープ状態になると、コンシューマまたはプロデューサをブロックします。ただし、このブロック機能にはコストが伴います。すべてのputまたはtake操作は、プロデューサーまたはコンシューマ(ある場合)の間でロック競合するため、多くのプロデューサー/消費者がいるシナリオでは、操作が遅くなる可能性があります。
ConcurrentLinkedQueue
はロックを使用していませんが、 [〜#〜] cas [〜#〜] で、そのput/take操作で、多くのプロデューサーおよびコンシューマースレッドとの競合を減らす可能性があります。しかし、「待機なし」のデータ構造であるため、ConcurrentLinkedQueue
は空のときにブロックしません。つまり、消費者はtake()
を処理して、_busyでnull
値を返す必要があります。たとえば、コンシューマスレッドがCPUを消費します。
そのため、どちらが「優れている」かは、コンシューマスレッドの数、消費/生成するレートなどに依存します。各シナリオにはベンチマークが必要です。
ConcurrentLinkedQueue
が明らかに優れている特定のユースケースは、プロデューサーが最初に何かを生成し、キューの後に消費者は、キューが空になったときに実行されることを認識して消費を開始します。 (ここでは、生産者と消費者の間に並行性はありませんが、生産者と生産者と消費者と消費者の間にのみあります)
別の解決策(適切にスケーリングされない)はランデブーチャネルです:Java.util.concurrent SynchronousQueue
キューが展開可能でなく、プロデューサー/コンシューマースレッドが1つだけ含まれている場合。ロックレスキューを使用できます(データアクセスをロックする必要はありません)。