Leitfaden für PriorityBlockingQueue in Java

Anleitung zu PriorityBlockingQueue in Java

1. Einführung

In diesem Artikel konzentrieren wir uns auf die KlassePriorityBlockingQueueund gehen einige praktische Beispiele durch.

Ausgehend von der Annahme, dass wir bereits wissen, was einQueue ist, werden wir zuersthow elements in the PriorityBlockingQueue are ordered by priority demonstrieren.

Im Anschluss wird gezeigt, wie dieser Warteschlangentyp zum Blockieren eines Threads verwendet werden kann.

Abschließend wird gezeigt, wie die gemeinsame Verwendung dieser beiden Funktionen bei der Verarbeitung von Daten über mehrere Threads hinweg nützlich sein kann.

2. Priorität der Elemente

Im Gegensatz zu einer Standardwarteschlange können SiePriorityBlockingQueue.nicht einfach einen beliebigen Elementtyp hinzufügen. Es gibt zwei Optionen:

  1. Hinzufügen von Elementen, dieComparable implementieren

  2. Hinzufügen von Elementen, dieComparable nicht implementieren, unter der Bedingung, dass Sie auchComparator angeben

Durch Verwenden der ImplementierungenComparator oderComparable zum Vergleichen von Elementen werden diePriorityBlockingQueue immer sortiert.

Ziel ist es, die Vergleichslogik so zu implementieren, dassthe highest priority element is always ordered first. Wenn wir dann ein Element aus unserer Warteschlange entfernen, ist es immer das Element mit der höchsten Priorität.

Lassen Sie uns zunächst unsere Warteschlange in einem einzelnen Thread verwenden, anstatt sie über mehrere Threads hinweg zu verwenden. Auf diese Weise können Sie auf einfache Weise nachweisen, wie Elemente in einem Komponententest angeordnet sind:

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);

Wie wir sehen können, werden die Elemente, obwohl sie in zufälliger Reihenfolge zur Warteschlange hinzugefügt wurden, sortiert, sobald wir sie abrufen. Dies liegt daran, dass die KlasseIntegerComparable, implementiert, die wiederum verwendet werden, um sicherzustellen, dass sie in aufsteigender Reihenfolge aus der Warteschlange entfernt werden.

Es ist auch erwähnenswert, dasswhen two elements are compared and are the same, there’s no guarantee of how they will be ordered.

3. Verwenden SieQueue zum Blockieren

Wenn wir es mit einer Standardwarteschlange zu tun hätten, würden wirpoll() aufrufen, um Elemente abzurufen. Wenn die Warteschlange jedoch leer wäre, würde ein Aufruf vonpoll()null. zurückgeben

DasPriorityBlockingQueue implementiert dieBlockingQueue-Schnittstelle, wodurch wir einige zusätzliche Methoden erhalten, mit denen wirblock when removing from an empty queue erreichen können. Versuchen wir, die Methodetake()zu verwenden, die genau das tun sollte:

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);

Obwohl die Verwendung vonsleep() eine etwas spröde Art ist, Dinge zu demonstrieren, werden wir beim Ausführen dieses Codes Folgendes sehen:

Polling...
Adding to queue
Polled: 1

Dies beweist, dasstake() blockiert sind, bis ein Element hinzugefügt wurde:

  1. Der Thread gibt "Polling" aus, um zu beweisen, dass er gestartet wurde

  2. Der Test wird dann etwa fünf Sekunden lang angehalten, um zu beweisen, dass der Thread zu diesem Zeitpunkttake() aufgerufen haben muss

  3. Wir fügen der Warteschlange hinzu und sollten mehr oder weniger sofort "Polled: 1" sehen, um zu beweisen, dasstake() ein Element zurückgegeben hat, sobald es verfügbar ist

Erwähnenswert ist auch, dass dieBlockingQueue-Schnittstelle uns auch Möglichkeiten zum Blockieren beim Hinzufügen zu vollständigen Warteschlangen bietet.

EinPriorityBlockingQueue ist jedoch unbegrenzt. Dies bedeutet, dass es niemals voll sein wird und somit immer neue Elemente hinzugefügt werden können.

4. Blockieren und Priorisieren zusammen verwenden

Nachdem wir die beiden Schlüsselkonzepte vonPriorityBlockingQueue,erklärt haben, verwenden wir beide zusammen. Wir können unser vorheriges Beispiel einfach erweitern, diesmal jedoch der Warteschlange weitere Elemente hinzufügen:

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));

Auch wenn dies aufgrund der Verwendung vonsleep(), etwas spröde ist, zeigt es uns dennoch einen gültigen Anwendungsfall. Wir haben jetzt eine Warteschlange, die blockiert und darauf wartet, dass Elemente hinzugefügt werden. Wir fügen dann viele Elemente gleichzeitig hinzu und zeigen dann, dass sie in der Prioritätsreihenfolge behandelt werden. Die Ausgabe sieht folgendermaßen aus:

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

5. Fazit

In diesem Handbuch haben wir gezeigt, wie wirPriorityBlockingQueue verwenden können, um einen Thread zu blockieren, bis einige Elemente hinzugefügt wurden, und dass wir diese Elemente basierend auf ihrer Priorität verarbeiten können.

Die Implementierung dieser Beispiele kannover on GitHub gefunden werden. Dies ist ein Maven-basiertes Projekt, sollte also so wie es ist einfach auszuführen sein.