web-dev-qa-db-ja.com

LinkedBlockingQueue vs ConcurrentLinkedQueue

私の質問は、 この質問 以前に尋ねたものに関連しています。プロデューサーとコンシューマースレッド間の通信にキューを使用している状況では、一般的にLinkedBlockingQueueまたはConcurrentLinkedQueueを使用することをお勧めしますか?

一方を他方より使用することの利点/欠点は何ですか?

APIの観点から見た主な違いは、LinkedBlockingQueueをオプションで制限できることです。

101
Adamski

プロデューサー/コンシューマースレッドの場合、ConcurrentLinkedQueueが合理的なオプションであることもわかりません。プロデューサー/コンシューマーキューIMOの基本的なインターフェイスであるBlockingQueueを実装していません。 poll()を呼び出して、何も見つからなかった場合は少し待ってから、もう一度ポーリングする必要があります...新しいアイテムが入ってくると遅延が発生し、空の場合は非効率になります睡眠から不必要に目覚めるため)。

BlockingQueueのドキュメントから:

BlockingQueue実装は、主に生産者/消費者キューに使用されるように設計されています

私はそれがstrictlyではないことを知っています。プロデューサー-コンシューマーキューにはブロッキングキューのみを使用する必要があると言いますが、それでも...

99
Jon Skeet

この質問はより良い答えに値します。

JavaのConcurrentLinkedQueueは、有名な Maged M. MichaelとMichael L. Scottのアルゴリズム for non-blocking lock-free キューに基づいています。

競合リソース(キュー)の用語としての「非ブロック」とは、スレッドの割り込みなど、プラットフォームのスケジューラーの動作に関係なく、または問題のスレッドが単に遅すぎる場合、他のスレッドが同じリソースを競合することを意味しますまだ進歩することができます。たとえば、ロックが関係している場合、ロックを保持しているスレッドが中断され、そのロックを待機しているすべてのスレッドがブロックされます。 Javaの本質的なロック(synchronizedキーワード)もパフォーマンスに深刻なペナルティをもたらす可能性があります- バイアスロック が関係している場合など)競合、またはVMがスピン猶予期間後、ロックを「膨張」させ、競合するスレッドをブロックすることを決定した後...多くのコンテキスト(低/中競合のシナリオ)で、アトミック参照の比較と設定ははるかに効率的であり、これはまさに多くのノンブロッキングデータ構造が行っていることです。

JavaのConcurrentLinkedQueueはノンブロッキングであるだけでなく、プロデューサーがコンシューマーと競合しないという素晴らしい特性を持っています。単一のプロデューサー/単一のコンシューマーシナリオ(SPSC)では、これは実際に言うべき競合がないことを意味します。複数のプロデューサー/単一のコンシューマーのシナリオでは、コンシューマーはプロデューサーと競合しません。複数のプロデューサーがoffer()を試行する場合、このキューには競合がありますが、それは定義による同時性です。基本的には汎用で効率的なノンブロッキングキューです。

BlockingQueueでないことに関しては、スレッドをブロックしてキューで待機させることは、並行システムを設計するひどく恐ろしい方法です。しないでください。コンシューマ/プロデューサーシナリオでConcurrentLinkedQueueを使用する方法がわからない場合は、優れたアクターフレームワークのように、より高レベルの抽象化に切り替えてください。

62

LinkedBlockingQueueは、キューが空または満杯で、それぞれのコンシューマ/プロデューサスレッドがスリープ状態になると、コンシューマまたはプロデューサをブロックします。ただし、このブロック機能にはコストが伴います。すべてのputまたはtake操作は、プロデューサーまたはコンシューマ(ある場合)の間でロック競合するため、多くのプロデューサー/消費者がいるシナリオでは、操作が遅くなる可能性があります。

ConcurrentLinkedQueueはロックを使用していませんが、 [〜#〜] cas [〜#〜] で、そのput/take操作で、多くのプロデューサーおよびコンシューマースレッドとの競合を減らす可能性があります。しかし、「待機なし」のデータ構造であるため、ConcurrentLinkedQueueは空のときにブロックしません。つまり、消費者はtake()を処理して、_busyでnull値を返す必要があります。たとえば、コンシューマスレッドがCPUを消費します。

そのため、どちらが「優れている」かは、コンシューマスレッドの数、消費/生成するレートなどに依存します。各シナリオにはベンチマークが必要です。

ConcurrentLinkedQueueが明らかに優れている特定のユースケースは、プロデューサーが最初に何かを生成し、キューの後に消費者は、キューが空になったときに実行されることを認識して消費を開始します。 (ここでは、生産者と消費者の間に並行性はありませんが、生産者と生産者と消費者と消費者の間にのみあります)

25
dcernahoschi

別の解決策(適切にスケーリングされない)はランデブーチャネルです:Java.util.concurrent SynchronousQueue

1
Ovidiu Lupas

キューが展開可能でなく、プロデューサー/コンシューマースレッドが1つだけ含まれている場合。ロックレスキューを使用できます(データアクセスをロックする必要はありません)。

0
Rahul