Handbuch zu PriorityBlockingQueue in Java

1. Einleitung

In diesem Artikel konzentrieren wir uns auf die PriorityBlockingQueue- Klasse und gehen einige praktische Beispiele durch.

Ausgehend von der Annahme, dass wir bereits wissen, was eine Warteschlange ist, werden wir zunächst zeigen, wie Elemente in der PriorityBlockingQueue nach Priorität geordnet sind .

Anschließend zeigen wir, wie diese Art von Warteschlange zum Blockieren eines Threads verwendet werden kann.

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

2. Priorität der Elemente

Im Gegensatz zu einer Standardwarteschlange können Sie einer PriorityBlockingQueue nicht einfach einen beliebigen Elementtyp hinzufügen . Es gibt zwei Möglichkeiten:

  1. Hinzufügen von Elementen, die Comparable implementieren
  2. Hinzufügen von Elementen , die nicht implementieren Comparable , unter der Bedingung , dass Sie einen bieten Vergleicher sowie

Wenn Sie entweder die Comparator- oder die Comparable- Implementierung zum Vergleichen von Elementen verwenden, wird die PriorityBlockingQueue immer sortiert.

Ziel ist es, die Vergleichslogik so zu implementieren, dass das Element mit der höchsten Priorität immer zuerst geordnet wird . Wenn wir dann ein Element aus unserer Warteschlange entfernen, ist es immer das 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 leicht 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, wenn wir mit der Abfrage beginnen. Dies liegt daran, dass die Integer- Klasse Comparable implementiert , was wiederum verwendet wird, um sicherzustellen, dass sie in aufsteigender Reihenfolge aus der Warteschlange entfernt werden.

Es ist auch erwähnenswert, dass beim Vergleich zweier Elemente, die gleich sind, keine Garantie dafür besteht, wie sie bestellt werden.

3. Verwenden der Warteschlange zum Blockieren

Wenn wir es mit einer Standardwarteschlange zu tun hätten, würden wir poll () aufrufen , um Elemente abzurufen. Wenn jedoch die Warteschlange leer ist, ein Aufruf zur Umfrage () zurückkehren würde null.

Die PriorityBlockingQueue implementiert die BlockingQueue- Schnittstelle, die uns einige zusätzliche Methoden bietet, mit denen wir beim Entfernen aus einer leeren Warteschlange blockieren können . Versuchen wir es mit der take () -Methode, 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 von sleep () 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, dass take () blockiert ist, 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 Zeitpunkt take () aufgerufen haben muss
  3. Wir fügen der Warteschlange hinzu und sollten mehr oder weniger sofort "Polled: 1" sehen, um zu beweisen, dass take () ein Element zurückgegeben hat, sobald es verfügbar ist

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

Eine PriorityBlockingQueue ist jedoch unbegrenzt. Dies bedeutet, dass es niemals voll sein wird, so dass es immer möglich ist, neue Elemente hinzuzufügen.

4. Blockieren und Priorisieren zusammen verwenden

Nachdem wir die beiden Schlüsselkonzepte einer PriorityBlockingQueue erläutert haben, verwenden wir beide zusammen. Wir können einfach unser vorheriges Beispiel erweitern, aber diesmal fügen wir der Warteschlange weitere Elemente hinzu:

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 von sleep () 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. Schlussfolgerung

In diesem Handbuch haben wir gezeigt, wie wir eine PriorityBlockingQueue 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 finden Sie auf GitHub. Dies ist ein Maven-basiertes Projekt und sollte daher so wie es ist einfach auszuführen sein.