JavaでのPriorityBlockingQueueへのガイド

JavaのPriorityBlockingQueueのガイド

1. 前書き

この記事では、PriorityBlockingQueueクラスに焦点を当て、いくつかの実用的な例について説明します。

Queueが何であるかをすでに知っているという仮定から始めて、最初にhow elements in the PriorityBlockingQueue are ordered by priorityを示します。

これに続いて、このタイプのキューを使用してスレッドをブロックする方法を示します。

最後に、複数のスレッドでデータを処理するときに、これらの2つの機能を一緒に使用することがどのように役立つかを示します。

2. 要素の優先順位

標準のキューとは異なり、PriorityBlockingQueue.に任意のタイプの要素を追加することはできません。2つのオプションがあります。

  1. Comparableを実装する要素の追加

  2. Comparatorも指定することを条件に、Comparableを実装しない要素を追加する

ComparatorまたはComparableの実装を使用して要素を比較することにより、PriorityBlockingQueueは常にソートされます。

目的は、the highest priority element is always ordered firstの方法で比較ロジックを実装することです。 次に、キューから要素を削除すると、常に最も高い優先度を持つ要素になります。

まず、キューを複数のスレッドで使用するのではなく、単一のスレッドで使用してみましょう。 これにより、単体テストで要素がどのように順序付けられているかを簡単に証明できます。

PriorityBlockingQueue queue = new PriorityBlockingQueue<>();
ArrayList polledElements = new ArrayList<>();

queue.add(1);
queue.add(5);
queue.add(2);
queue.add(3);
queue.add(4);

queue.drainTo(polledElements);

assertThat(polledElements).containsExactly(1, 2, 3, 4, 5);

ご覧のように、要素をキューにランダムな順序で追加しても、ポーリングを開始すると要素が順序付けられます。 これは、IntegerクラスがComparable,を実装しているためです。これは、昇順でキューからそれらを確実に取り出すために使用されます。

when two elements are compared and are the same, there’s no guarantee of how they will be ordered.も注目に値します

3. Queueを使用してブロックする

標準キューを処理している場合は、poll()を呼び出して要素を取得します。 ただし、キューが空の場合、poll()を呼び出すとnull.が返されます。

PriorityBlockingQueueBlockingQueueインターフェースを実装します。これにより、block when removing from an empty queueを可能にするいくつかの追加メソッドが提供されます。 take()メソッドを使用してみましょう。これにより、次のようになります。

PriorityBlockingQueue queue = new PriorityBlockingQueue<>();

new Thread(() -> {
  System.out.println("Polling...");

  try {
      Integer poll = queue.take();
      System.out.println("Polled: " + poll);
  } catch (InterruptedException e) {
      e.printStackTrace();
  }
}).start();

Thread.sleep(TimeUnit.SECONDS.toMillis(5));
System.out.println("Adding to queue");
queue.add(1);

sleep()を使用することは、物事を示すためのやや脆弱な方法ですが、このコードを実行すると、次のようになります。

Polling...
Adding to queue
Polled: 1

これは、アイテムが追加されるまでtake()がブロックされたことを証明します。

  1. スレッドは「ポーリング」を出力して、開始されたことを証明します

  2. その後、テストは約5秒間一時停止し、この時点までにスレッドがtake()を呼び出している必要があることを証明します。

  3. キューに追加すると、多かれ少なかれ即座に「Polled:1」が表示され、take()が要素が使用可能になるとすぐに要素を返したことを証明するはずです

BlockingQueueインターフェースは、完全なキューに追加するときにブロックする方法も提供することにも言及する価値があります。

ただし、PriorityBlockingQueueには制限がありません。 これは、それがいっぱいになることは決してないことを意味し、したがって、常に新しい要素を追加することができます。

4. ブロッキングと優先順位付けを一緒に使用する

PriorityBlockingQueue,の2つの重要な概念について説明したので、両方を一緒に使用しましょう。 前の例を単純に拡張できますが、今回はキューにさらに要素を追加します。

Thread thread = new Thread(() -> {
    System.out.println("Polling...");
    while (true) {
        try {
            Integer poll = queue.take();
            System.out.println("Polled: " + poll);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

thread.start();

Thread.sleep(TimeUnit.SECONDS.toMillis(5));
System.out.println("Adding to queue");

queue.addAll(newArrayList(1, 5, 6, 1, 2, 6, 7));
Thread.sleep(TimeUnit.SECONDS.toMillis(1));

繰り返しますが、これはsleep(),を使用しているため少し脆弱ですが、それでも有効なユースケースを示しています。 ブロックされるキューがあり、要素が追加されるのを待っています。 次に、一度に多くの要素を追加し、それらが優先順に処理されることを示します。 出力は次のようになります。

Polling...
Adding to queue
Polled: 1
Polled: 1
Polled: 2
Polled: 5
Polled: 6
Polled: 6
Polled: 7

5. 結論

このガイドでは、いくつかのアイテムが追加されるまでスレッドをブロックするためにPriorityBlockingQueueを使用する方法と、それらのアイテムを優先度に基づいて処理できることを示しました。

これらの例の実装はover on GitHubにあります。 これはMavenベースのプロジェクトなので、そのまま実行するのは簡単です。