web-dev-qa-db-ja.com

ConcurrentLinkedQueueの使用方法

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


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

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

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

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

89

いいえ、メソッドを同期する必要はありません。また、メソッドを定義する必要もありません。それらは既に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を使用するだけです。アプリケーションにとってはまだやり過ぎですが、うまくいく可能性があります。十分なパフォーマンスが得られない場合(PROFILE!)、いつでも別のことを試すことができます。つまり、同期されたものを扱う必要はありません。

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.

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

150
Adam Jaskiewicz

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

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

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

並行コレクションのアトミック操作は自動的に同期されます。つまり、キューへの個々の呼び出しは、ユーザー側で何のアクションもせずにスレッドセーフであることが保証されています。 notスレッドセーフであることが保証されるのは、コレクションに対して実行する非アトミックな操作です。

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

queue.add(obj);

または

queue.poll(obj);

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

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

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

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

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

33
Jared

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

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

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

6
marq

poll を使用して最初の要素を取得し、 add を使用して新しい最後の要素を追加します。それだけです、同期などはありません。

6
Hank Gay

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

2
siddhadev

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

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

1
Ben S