Javaではどの並行キュー実装を使用する必要がありますか?


132

JavaDocsから:

  • A ConcurrentLinkedQueueは、多くのスレッドが共通のコレクションへのアクセスを共有する適切な選択です。このキューはnull要素を許可しません。
  • ArrayBlockingQueueは、固定サイズの配列がプロデューサーによって挿入され、コンシューマーによって抽出された要素を保持する、古典的な「境界付きバッファー」です。このクラスは、待機中のプロデューサースレッドとコンシューマースレッドを注文するためのオプションの公平性ポリシーをサポートします
  • LinkedBlockingQueueは、通常、アレイベースのキューよりもスループットは高くなりますが、ほとんどの並行アプリケーションでは予測できないパフォーマンスになります。

私には2つのシナリオがあります。1つは、1つのコンシューマーで多くのプロデューサー(それを使用するスレッド)をサポートするキューを必要とし、もう1つはその逆です。

使用する実装がわかりません。誰かが違いを説明できますか?

また、「オプションの公平性ポリシー」とはArrayBlockingQueue何ですか?


1
PriorityBlockingQueueについても質問するのを忘れていました。これは、スレッドが処理される順序を指定するのに役立ちます。
IgorGanapolsky 14

回答:


53

基本的に、それらの違いはパフォーマンス特性とブロッキング動作です。

最も簡単なのArrayBlockingQueueは、固定サイズのキューです。したがって、サイズを10に設定し、11番目の要素を挿入しようとすると、挿入ステートメントは、別のスレッドが要素を削除するまでブロックされます。公平性の問題は、複数のスレッドが同時に(つまり、キューがブロックされていた期間に)挿入と削除を試みた場合に発生します。公平性アルゴリズムにより、最初に要求するスレッドが最初に取得するスレッドになります。そうしないと、特定のスレッドが他のスレッドよりも長く待機し、予期しない動作が発生する可能性があります(後で開始した他のスレッドが最初に処理されるため、1つのスレッドが数秒かかることもあります)。トレードオフは、公平性を管理するためにオーバーヘッドがかかり、スループットが低下することです。

との最も重要な違いはLinkedBlockingQueueConcurrentLinkedQueueaから要素をリクエストLinkedBlockingQueueし、キューが空の場合、スレッドは何かがあるまで待機することです。A ConcurrentLinkedQueueは空のキューの動作ですぐに戻ります。

ブロッキングが必要かどうかはどちらに依存します。多くのプロデューサーと1つのコンシューマーがある場合、それはそのように聞こえます。一方、多数のコンシューマがあり、プロデューサが1つしかない場合、ブロッキング動作は必要なく、コンシューマにキューが空かどうかを確認させ、キューが空の場合は次に進むだけでよい場合があります。


67
答えは誤解を招くものです。LinkedBlockingQueueとConcurrentLinkedQueueの両方に、キューの先頭を削除するかnullを返す(ブロックしない)メソッド "poll()"と、キューの末尾に挿入してブロックしない "offer(E e)"メソッドがあります。違いは、LinkedBlockingQueueだけが非ブロック操作に加えてブロック操作持っていることです。そしてその特権については、LinkedBlockingQueueが実際にいくらかのロックを持っているという代価を支払います。他の答えはこれを説明しています。
Nakedible、

123

ConcurrentLinkedQueueは、ロックが取得されない(つまり、synchronized(this)またはLock.lock呼び出しがない)ことを意味します。変更中にCAS-Compare and Swap操作を使用して、ヘッド/テールノードが開始時と同じかどうかを確認します。その場合、操作は成功します。ヘッド/テールノードが異なる場合は、スピンして再試行します。

LinkedBlockingQueueは変更前にロックを取得します。したがって、オファーの呼び出しは、ロックを取得するまでブロックされます。TimeUnitを使用するオファーオーバーロードを使用して、追加を中止する前にX時間だけ待機してもよいと言うことができます(通常、メッセージがXミリ秒後に古くなるメッセージタイプのキューに適しています)。

公平性とは、ロックの実装によってスレッドの順序が維持されることを意味します。スレッドAに入り、次にスレッドBが入ると、スレッドAが最初にロックを取得します。公平性がないため、実際に何が起こるかは定義されていません。それはおそらくスケジュールされる次のスレッドです。

どちらを使用するかは、場合によって異なります。プロデューサーが作業をキューに入れるのにかかる時間は多様であるため、私はConcurrentLinkedQueueを使用する傾向があります。まったく同じ時期にプロデュースしているプロデューサーは少ないです。しかし、世論調査が適切なスリープ状態にならないため、消費者側はより複雑になります。自分で処理する必要があります。


1
そして、どのような条件下でArrayBlockingQueueがLinkedBlockingQueueよりも優れていますか?
kolobok 2012

@akapelko ArrayBlockingQueueは、よりきめ細かい順序付けを可能にします。
IgorGanapolsky 14

2
それはどういう意味ですか-「それは回転して再試行します。」?
レスター

9

質問のタイトルで、ブロッキングキューについて言及されています。ただし、ブロッキングキューでConcurrentLinkedQueueはありません

BlockingQueuesはArrayBlockingQueueDelayQueueLinkedBlockingDequeLinkedBlockingQueuePriorityBlockingQueue、とSynchronousQueue

これらのいくつかは(あなたの目的に合わないはっきりしているDelayQueuePriorityBlockingQueueSynchronousQueue)。 LinkedBlockingQueueそしてLinkedBlockingDeque後者は両端キュー(それのDequeインタフェースを実装する)であることを除いて、同一です。

ArrayBlockingQueue要素の数を制限したい場合にのみ役立つので、私はに固執しLinkedBlockingQueueます。


タイトルからブロッキングワードを削除しました。ありがとうございます。あなたが言ったことは、同じオブジェクトを介してLinkedBlockingQueueを複数のコンシューマー/プロデュースシナリオで使用できることを意味しますか?
David Hofmann、

1
ArrayBlockingQueueを使用すると、スレッドをよりきめ細かく順序付けできると思いましたか?したがって、その利点。
IgorGanapolsky 14

4

ArrayBlockingQueueはメモリフットプリントが少なく、新しい挿入ごとにLinkedBlockingQueue $ Nodeオブジェクトを作成する必要があるLinkedBlockingQueueとは異なり、要素ノードを再利用できます。


1
いい視点ね!私はより多くのLinkedBlockingQueueよりもArrayBlockingQueueを好む

2
これは必ずしも当てはまりません-キューが空に近いことが多いのに、大きくする必要がある場合ArrayBlockingQueueは、メモリフットプリントがはるかに悪くなります。LinkedBlockingQueue空にしたときに近い無視できるメモリフットプリントを持っています。
Krease

1
  1. SynchronousQueue (別から撮影 質問

SynchronousQueueLinkedBlockingQueue単なるハンドオフですが、は単一の要素を許可します。差があることput()への呼び出しがSynchronousQueue対応があるまで戻らないtake()コールが、しかしとLinkedBlockingQueueサイズ1の、put()(空のキューに)呼び出しは直ちに戻ります。これは基本的に、BlockingQueue本当にキューが必要ない場合(保留中のデータを保持したくない場合)の実装です。

  1. LinkedBlockingQueueLinkedList実装ですが、厳密にはJDK実装でLinkedListは、要素間のリンクを維持するために静的内部クラスNodeを使用しています)

LinkedBlockingQueueのコンストラクタ

public LinkedBlockingQueue(int capacity) 
{
        if (capacity < = 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node< E >(null);   // Maintains a underlying linkedlist. ( Use when size is not known )
}

リンクの維持に使用されるノードクラス

static class Node<E> {
    E item;
    Node<E> next;
    Node(E x) { item = x; }
}

3。ArrayBlockingQueue(配列の実装)

ArrayBlockingQueueのコンストラクタ

public ArrayBlockingQueue(int capacity, boolean fair) 
{
            if (capacity < = 0)
                throw new IllegalArgumentException();
            this.items = new Object[capacity]; // Maintains a underlying array
            lock = new ReentrantLock(fair);
            notEmpty = lock.newCondition();
            notFull =  lock.newCondition();
}

間私見最大差ArrayBlockingQueueとは、LinkedBlockingQueue一つたコンストラクタから明らかなデータ構造配列と他のLinkedListの基礎となります

ArrayBlockingQueueシングルロックダブル条件アルゴリズムを使用しLinkedBlockingQueue、「2ロックキュー」アルゴリズムのバリアントであり、2つのロックがあり、2つの条件(takeLock、putLock)


0

ConcurrentLinkedQueueはロックフリーですが、LinkedBlockingQueueはロックフリーではありません。LinkedBlockingQueue.put()またはLinkedBlockingQueue.take()を呼び出すたびに、最初にロックを取得する必要があります。つまり、LinkedBlockingQueueの同時実行性は低くなっています。パフォーマンスが気になる場合は、ConcurrentLinkedQueue + LockSupportを試してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.