

In diesem und den nächsten Teilen dieser Tutorialserie geht es um Queues für spezielle Einsatzzwecke. Wir beginnen mit der DelayQueue
, einer Queue, die die Elemente nach Ablaufzeit sortiert herausgibt.
Hier befinden wir uns in der Klassenhierarchie:

DelayQueue Eigenschaften
Die Klasse java.util.concurrent.DelayQueue
ist – genau wie die PriorityQueue, die sie intern verwendet – keine FIFO-Queue. Es wird nicht das Element entnommen, das sich am längsten in der Queue befindet. Stattdessen wird ein Element dann entnommen, wenn eine diesem Element zugeordnete Wartezeit ("delay") abgelaufen ist.
Dazu müssen die Elemente das Interface java.util.concurrent.Delayed
und dessen getDelay()
-Methode implementieren. Diese Methode liefert bei jeden Aufruf die restliche Wartezeit zurück, die noch ablaufen muss, bis das Element aus der Queue entnommen werden darf.
DelayQueue
ist blockierend, aber unbounded, kann also beliebig viele Elemente aufnehmen und blockiert nur bei der Entnahme (bis die Wartezeit abgelaufen ist), nicht beim Einfügen.
Threadsicherheit wird durch pessimistisches Locking über ein einzelnes ReentrantLock
sichergestellt.
Die Eigenschaften im Detail:
Unterliegende Datenstruktur | Thread-safe? | Blocking/ Non-blocking | Fairness Policy | Bounded/ Unbounded | Iterator Type |
---|---|---|---|---|---|
Priority Queue | Ja (pessimistisches Locking mit einem Lock) | Blocking (nur dequeue) | Nicht verfügbar | Unbounded | Weakly consistent¹ |
¹ Weakly consistent: Alle Elemente, die zum Zeitpunkt der Erzeugung des Interators in der Queue liegen, werden vom Iterator genau einmal durchlaufen. Änderungen, die danach erfolgen, können – müssen aber nicht – durch den Iterator berücksichtigt werden.
Einsatzempfehlung
Ich habe die DelayQueue
noch nie benötigt und kann sie für keinen mir bekannten, sinnvollen Einsatzzweck empfehlen. Sie wird im JDK nur ein einziges Mal genutzt (in einer alten Swing-Klasse, die mit einem ScheduledExecutorService
wahrscheinlich eleganter hätte implementiert werden können). Von daher ist nicht ausgeschlossen, dass sie unentdeckte Fehler enthält.
DelayQueue Beispiel
In folgendem Beispiel (→ Code auf GitHub) wird eine DelayQueue
mit Instanzen der Klasse DelayedElement
gefüllt, welche eine Zufallszahl enthalten sowie ein zufälliges initiales Delay zwischen 100 und 1.000 ms. Danach wird so oft poll()
aufgerufen, bis die Queue wieder leer ist.
public class DelayQueueExample {
public static void main(String[] args) {
BlockingQueue<DelayedElement<Integer>> queue = new DelayQueue<>();
ThreadLocalRandom random = ThreadLocalRandom.current();
long startTime = System.currentTimeMillis();
// Enqueue random numbers with random initial delays
for (int i = 0; i < 7; i++) {
int randomNumber = random.nextInt(10, 100);
int initialDelayMillis = random.nextInt(100, 1000);
DelayedElement<Integer> element =
new DelayedElement<>(randomNumber, initialDelayMillis);
queue.offer(element);
System.out.printf(
"[%3dms] queue.offer(%s) --> queue = %s%n",
System.currentTimeMillis() - startTime, element, queue);
}
// Dequeue all elements
while (!queue.isEmpty()) {
try {
DelayedElement<Integer> element = queue.take();
System.out.printf(
"[%3dms] queue.poll() = %s --> queue = %s%n",
System.currentTimeMillis() - startTime, element, queue);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
Code-Sprache: Java (java)
Und hier die zugehörige Klasse DelayedElement
(→ Code auf GitHub). Die Sortierung ist – um den Code nicht noch länger zu machen – nicht stabil. D. h. wenn zwei Elemente mit derselben Wartezeit in die Queue gestellt werden, werden sie in zufälliger Reihenfolge wieder entnommen.
public class DelayedElement<E extends Comparable<E>> implements Delayed {
private final E e;
private final long initialDelayMillis;
private final long expiration;
public DelayedElement(E e, long initialDelayMillis) {
this.e = e;
this.initialDelayMillis = initialDelayMillis;
this.expiration = System.currentTimeMillis() + initialDelayMillis;
}
@Override
public long getDelay(TimeUnit unit) {
long remainingDelayMillis = expiration - System.currentTimeMillis();
return unit.convert(remainingDelayMillis, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
DelayedElement<?> other = (DelayedElement<?>) o;
return Long.compare(expiration, other.expiration);
}
@Override
public String toString() {
return "{%s, %dms}".formatted(e, initialDelayMillis);
}
}
Code-Sprache: Java (java)
Hier eine beispielhafte Ausgabe des Programms. Es ist gut zu sehen, wie die Queue zwar nicht sortiert ist¹, das Element mit der kürzesten Wartezeit sich jedoch immer vorne (links) befindet und dass die Elemente (ungefähr) nach Ablauf ihrer jeweiligen Wartezeit entnommen werden:
[ 1ms] queue.offer({19, 713ms}) --> queue = [{19, 713ms}]
[ 28ms] queue.offer({15, 643ms}) --> queue = [{15, 643ms}, {19, 713ms}]
[ 29ms] queue.offer({35, 253ms}) --> queue = [{35, 253ms}, {19, 713ms}, {15, 643ms}]
[ 29ms] queue.offer({11, 781ms}) --> queue = [{35, 253ms}, {19, 713ms}, {15, 643ms}, {11, 781ms}]
[ 29ms] queue.offer({17, 844ms}) --> queue = [{35, 253ms}, {19, 713ms}, {15, 643ms}, {11, 781ms}, {17, 844ms}]
[ 29ms] queue.offer({40, 490ms}) --> queue = [{35, 253ms}, {19, 713ms}, {40, 490ms}, {11, 781ms}, {17, 844ms}, {15, 643ms}]
[ 30ms] queue.offer({39, 119ms}) --> queue = [{39, 119ms}, {19, 713ms}, {35, 253ms}, {11, 781ms}, {17, 844ms}, {15, 643ms}, {40, 490ms}]
[151ms] queue.poll() = {39, 119ms} --> queue = [{35, 253ms}, {19, 713ms}, {40, 490ms}, {11, 781ms}, {17, 844ms}, {15, 643ms}]
[283ms] queue.poll() = {35, 253ms} --> queue = [{40, 490ms}, {19, 713ms}, {15, 643ms}, {11, 781ms}, {17, 844ms}]
[520ms] queue.poll() = {40, 490ms} --> queue = [{15, 643ms}, {19, 713ms}, {17, 844ms}, {11, 781ms}]
[673ms] queue.poll() = {15, 643ms} --> queue = [{19, 713ms}, {11, 781ms}, {17, 844ms}]
[716ms] queue.poll() = {19, 713ms} --> queue = [{11, 781ms}, {17, 844ms}]
[811ms] queue.poll() = {11, 781ms} --> queue = [{17, 844ms}]
[874ms] queue.poll() = {17, 844ms} --> queue = []
Code-Sprache: Klartext (plaintext)
¹ Tatsächlich sieht man die Reihenfolge der Elemente in der Array-Repräsentation des Min-Heaps.
Zusammenfassung und Ausblick
In diesem Artikel hast du alles über die DelayQueue
, ihre Eigenschaften und ihre Verwendung erfahren.
Im nächsten Teil dieser Serie stelle ich dir weitere Spezialqueue vor – eine die niemals Elemente enthält: die SynchronousQueue.
Wenn du noch Fragen hast, stelle sie gerne über die Kommentar-Funktion. Möchtest du über neue Tutorials und Artikel informiert werden? Dann klicke hier, um dich für den HappyCoders.eu-Newsletter anzumelden.