ConcurrentLinkedQueueの使用方法


95

ConcurrentLinkedQueueJavaでどのように使用しますか?
これを使用LinkedQueueすると、キューの同時実行性を心配する必要がありますか?または、2つのメソッドを定義する必要がありますか(1つはリストから要素を取得するため、もう1つはリストに要素を追加するため)。
注:明らかにこれらの2つの方法は同期する必要があります。正しい?


編集:私がやろうとしているのはこれです:キューからアイテムを取得する1つのメソッドを持つクラス(Java)と、キューにアイテムを追加する1つのメソッドを持つ別のクラスがあります。リストに追加され、リストから取得されるアイテムは、自分のクラスのオブジェクトです。

もう1つの質問:removeメソッドでこれを行う必要がありますか?

while (queue.size() == 0){ 
  wait(); 
  queue.poll();
}

消費者と生産者はそれぞれ1人だけです。


mtの質問に対する回答をありがとう。私がやろうとしているのはこれです:キューからアイテムを取得する1つのメソッドを持つクラス(Java)と、キューにアイテムを追加する1つのメソッドを持つ別のクラスがあります。リストに追加され、リストから取得されるアイテムは、自分のクラスのオブジェクトです。
Ricardo Felgueiras 2009年

2
質問を編集し、この説明を質問自体に含める必要があります。
アダムJaskiewicz 2009年

回答:


156

いいえ、メソッドを同期する必要はなく、メソッドを定義する必要もありません。それらはすでにConcurrentLinkedQueueにあります、それらを使用してください。ConcurrentLinkedQueueは、内部で必要なすべてのロックおよびその他の操作を実行します。プロデューサーがキューにデータを追加し、コンシューマーがそれをポーリングします。

まず、キューを作成します。

Queue<YourObject> queue = new ConcurrentLinkedQueue<YourObject>();

ここで、プロデューサー/コンシューマーオブジェクトを作成する場所にキューを渡して、オブジェクトをどこかに配置できるようにします(代わりにセッターを使用できますが、コンストラクターでこの種のことを行うことをお勧めします)。

YourProducer producer = new YourProducer(queue);

そして:

YourConsumer consumer = new YourConsumer(queue);

そして、あなたのプロデューサーでそれにものを追加します:

queue.offer(myObject);

そして、コンシューマ内のものを取り出します(キューが空の場合、poll()はnullを返すので、チェックしてください)。

YourObject myObject = queue.poll();

詳細については参照してください 、Javadocを

編集:

キューが空になるのを待つのをブロックする必要がある場合は、おそらくLinkedBlockingQueueを使用する必要がありますを使用して、take()メソッドがあります。ただし、LinkedBlockingQueueには最大容量(デフォルトではInteger.MAX_VALUE、20億を超える)があるため、状況に応じて適切な場合とそうでない場合があります。

キューにデータを入れるスレッドが1つだけで、キューからデータを取り出す別のスレッドがある場合、ConcurrentLinkedQueueはおそらくやりすぎです。数百または数千のスレッドが同時にキューにアクセスする可能性がある場合に適しています。あなたのニーズはおそらく以下を使用することで満たされます:

Queue<YourObject> queue = Collections.synchronizedList(new LinkedList<YourObject>());

さらに、インスタンス(キュー)でロックされるため、キューで同期して、複合操作のアトミック性を確保できます(Jaredが説明)。すべての操作はインスタンスのロックなしで(java.util.concurrent.atomic変数を使用して)実行されるため、これをConcurrentLinkedQueueで行うことはできません。キューが空のときにブロックしたい場合は、これを行う必要はありません。なぜなら、poll()は、キューが空のときにnullを返すだけで、poll()はアトミックだからです。poll()がnullを返すかどうかを確認します。存在する場合は、wait()してから、再試行してください。ロックする必要はありません。

最後に:

正直なところ、私はLinkedBlockingQueueを使用します。それはあなたのアプリケーションにとってはまだやり過ぎですが、おそらくうまくいくでしょう。十分なパフォーマンスがない場合(プロファイル!)、いつでも別の方法を試すことができます。これは、同期されたものを処理する必要がないことを意味します。

BlockingQueue<YourObject> queue = new LinkedBlockingQueue<YourObject>();

queue.put(myObject); // Blocks until queue isn't full.

YourObject myObject = queue.take(); // Blocks until queue isn't empty.

他はすべて同じです。20億個のオブジェクトをキューに入れる可能性は低いので、Putはおそらくブロックしません。


御返答いただき有難うございます。もう1つの質問:removeメソッドでこれを行う必要がありますか:while(queue.size()== 0)wait(); queue.poll();
Ricardo Felgueiras 2009年

これはかなり重要なので、私の回答の編集として回答します。
アダムJaskiewicz 2009年

別の質問で混乱を引き起こしたようにCollection.synchronizedListListを実装しないを返しますQueue
トム・ホーティン-タックライン

@AdamJaskiewiczが使用しているConcurrentLinkedQueueプロデューサーの消費者のための良いアイデアを、私はこの記事を参照していますstackoverflow.com/questions/1426754/...
RD22

37

これは主に別の質問の複製です

この質問に関連する回答のセクションは次のとおりです。

java.util.ConcurrentLinkedQueueを使用する場合、独自の同期を行う必要がありますか?

並行コレクションのアトミック操作は同期されます。言い換えると、キューへの個々の呼び出しは、ユーザーのアクションなしでスレッドセーフであることが保証されます。何ではないスレッドセーフを保証することは、非アトミックあるコレクションで実行するすべての操作です。

たとえば、これはあなたの側で何のアクションもなしにスレッドセーフです:

queue.add(obj);

または

queue.poll(obj);

しかしながら; キューへの非アトミックな呼び出しは、自動的にスレッドセーフではありません。たとえば、次の操作は自動的にスレッドセーフではありません

if(!queue.isEmpty()) {
   queue.poll(obj);
}

最後のスレッドは、スレッドセーフではありません。isEmptyが呼び出されてからポーリングが呼び出されるまでの間に、他のスレッドがキューにアイテムを追加またはキューから削除した可能性が高いためです。これを実行するスレッドセーフな方法は次のとおりです。

synchronized(queue) {
    if(!queue.isEmpty()) {
       queue.poll(obj);
    }
}

繰り返しますが...キューへのアトミック呼び出しは自動的にスレッドセーフです。非アトミックコールはそうではありません。


1
私ができる唯一の一般的な用途は、キューが空でなくなるまでブロックすることです。それはBlockingQueueの実装を使用することでより効果的になりますか(take()はアトミックであり、消費するものが存在するまでブロックします)?
アダムJaskiewicz 2009年

6
また、注意が必要です。synchronizedListとは異なり、ConcurrentLinkedQueueはそれ自体では同期しないため、コードでは、同期されたブロック内にいる間にプロデューサーがキューに提供することも可能です。
アダムJaskiewicz 2009年

なぜqueue.isEmpty()とqueue.poll()を組み合わせるのですか?結果がnullかどうかをポーリングして確認できませんか?(私の理解では、空のキューをポーリングするとnullが返される)
AjahnCharles 2017

最後のコードブロックを除いて、コードのすべてに同意します。他の呼び出しは同期されないため、ConcurrentLInkedQueueで同期しても何も保証されません。だから、「(..)を追加し、」あなたはこのスレッドを同期しているにもかかわらず、「(...)世論調査」あなた「のisEmpty()」との間に別のスレッドから起こっがあるかもしれない
Klitos G.


6

これはおそらく、キュー内のすべてを消費しようとするときに、スレッドの安全性と「かわいさ」の点で探しているものです。

for (YourObject obj = queue.poll(); obj != null; obj = queue.poll()) {
}

これにより、キューが空のときに終了し、空でない限りオブジェクトをポップし続けることが保証されます。


キューを空にするのに非常に役立ちます。ありがとう。
DevilCode

2

ConcurentLinkedQueueは非常に効率的な待機/ロックフリーの実装であり(参照についてはjavadocを参照)、同期する必要がないだけでなく、キューは何もロックせず、非同期(スレッドではない)とほぼ同じ速さ安全)1。


1

非並行コレクションと同じように使用してください。Concurrent [Collection]クラスは通常のコレクションをラップするため、アクセスの同期について考える必要はありません。

編集: ConcurrentLinkedListは実際には単なるラッパーではなく、より優れた並行実装です。どちらの方法でも、同期について心配する必要はありません。


ConcurrentLinkedQueueはそうではありません。特に、複数のプロデューサーとコンシューマーによる同時アクセスのためにゼロから構築されています。Collections.synchronizedによって返されたシンプルなラッパー*よりもビット手の込んだ
アダムJaskiewicz

うん、J2SE 5で追加きちんと並行処理のものがたくさんありました
アダムJaskiewicz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.