This and the following parts of this tutorial series are about queues for particular purposes. We will start with DelayQueue
, a queue that sorts the elements by expiration time.
We are here in the class hierarchy:
DelayQueue Characteristics
The java.util.concurrent.DelayQueue
class – just like the PriorityQueue it uses internally – is not a FIFO queue. It does not take out the element that has been in the queue the longest. Instead, an element can be taken when a wait time ("delay") assigned to that element has expired.
Therefore, the elements must implement the interface java.util.concurrent.Delayed
and its getDelay()
method. This method returns the remaining waiting time that must elapse before the element can be removed from the queue.
DelayQueue
is blocking but unbounded, so it can hold any number of elements and blocks only on removal (until the wait time expires), not on insertion.
Thread safety is achieved by pessimistic locking via a single ReentrantLock
.
The characteristics in detail:
Underlying data structure | Thread-safe? | Blocking/ non-blocking | Fairness policy | Bounded/ unbounded | Iterator type |
---|---|---|---|---|---|
Priority queue | Yes (pessimistic locking with a lock) | Blocking (only dequeue) | Not available | Unbounded | Weakly consistent¹ |
¹ Weakly consistent: All elements that exist when the iterator is created are traversed by the iterator exactly once. Changes that occur after this can, but do not need to, be reflected by the iterator.
Recommended Use Case
I have never needed DelayQueue
and cannot recommend it for any practical purpose that I know of. It is used only once in the JDK (in an old Swing class that could have been implemented more elegantly with a ScheduledExecutorService
). Therefore, it may contain undiscovered bugs.
DelayQueue Example
In the following example (→ code on GitHub), we fill a DelayQueue
with instances of the DelayedElement
class. Those instances contain a random number and a random initial delay between 100 and 1,000 ms. Then we call poll()
until the queue is empty again.
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 language: Java (java)
And here is the corresponding DelayedElement
class (→ code on GitHub). In order not to make the code even longer, the sorting is not stable. I.e., if two elements with the same waiting time are inserted into the queue, they will be removed in random order relative to each other.
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 language: Java (java)
Here is an example output of the program. It is good to see how the queue is not sorted¹, but the element with the shortest waiting time is always at the head (left) and that the elements are taken (approximately) after their respective waiting times have expired:
[ 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 language: plaintext (plaintext)
¹ In fact, you can see the order of the elements in the array representation of the min-heap.
Summary and Outlook
In this article, you have learned everything about DelayQueue, its characteristics, and how to use it.
In the next part of this series, I will introduce you to another special queue – one that never contains any elements: SynchronousQueue.
If you still have questions, please ask them via the comment function. Do you want to be informed about new tutorials and articles? Then click here to sign up for the HappyCoders.eu newsletter.