Collections Framework – 4 [ Queue ]

The Queue interface defines functionality for a collection that stores items in FIFO(First In First Out) order.

It is an ordered list of objects with its use limited to inserting elements at the end of the list and deleting elements from the start of the list.

It offers methods for two different behaviors in case of failure of its operations : the return of a boolean indicating the success of the operation or the raising of an exception.

What You Need

  • About 21 minutes
  • A favorite text editor or IDE
  • Java 8 or later

1. Methods of Queue Interface

The Queue interface defines several methods :

MethodRole
E element()Retrieves, but does not remove, the head of this queue. This method differs from peek only in that it throws an exception if this queue is empty.
boolean offer(E o)Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions. When using a capacity-restricted queue, this method is generally preferable to add(E), which can fail to insert an element only by throwing an exception.
E peek()Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
E poll()Retrieves and removes the head of this queue, or returns null if this queue is empty.
E remove()Retrieves and removes the head of this queue. This method differs from poll only in that it throws an exception if this queue is empty.

2. Implementations of Queue Interface

ImplementationRole
java.util.concurrent.LinkedTransferQueue<E>Its ability is to transfer elements between threads in a way that blocks the producer until the element is consumed by a consumer. This transfer is done using the transfer() method, which will block the calling thread until the element has been consumed by a consumer thread.
java.util.concurrent.ArrayBlockingQueue<E>Its capacity is specified at the time of its creation and cannot be changed thereafter. When an element is added to a full ArrayBlockingQueue, the adding thread is blocked until a space becomes available. Similarly, when an element is removed from an empty ArrayBlockingQueue, the removing thread is blocked until an element becomes available.
java.util.concurrent.DelayQueue<E extends Delayed>It is a blocking queue that could be used in producer-consumer programs. It has a very useful characteristic – when the consumer wants to take an element from the queue, they can take it only when the delay for that particular element has expired.
java.util.concurrent.LinkedBlockingQueue<E>It is an optionally-bounded blocking queue implementation, meaning that the queue size can be specified if needed. The capacity, if unspecified, is equal to Integer.MAX_VALUE.
java.util.concurrent.SynchronousQueue<E>It is a blocking queue in which each insert operation must wait for a corresponding remove operation by another thread and vice versa.
java.util.concurrent.PriorityBlockingQueue<E>It is a unbounded queue which allows to block when removing from an empty queue. The elements in the PriorityBlockingQueue are ordered by priority. The highest priority element is always ordered first. When two elements are compared and are the same, there’s no guarantee of how they will be ordered.
java.util.PriorityQueue<E>The elements in PriorityQueue are ordered according to their natural ordering or a custom comparator. We can not just add any type of element to a PriorityQueue, either adding elements which implement Comparable or adding elements which do not implement Comparable but providing a Comparator as well.
java.util.ArrayDeque<E>Array Double Ended Queue is a special kind of a growable array that allows to add or remove an element from both sides. It can be used as a Stack (Last-In-First-Out) or a Queue (First-In-First-Out).
java.util.concurrent.LinkedBlockingDeque<E>It is a thread-safe and concurrent implementation of the Deque interface that uses a linked list to store its elements. It is optionally-bounded. When unspecified, the capacity is by default taken as Integer.MAX_VALUE. It is a BlockingDeque, which means that threads that try to insert elements into a full deque or remove elements from an empty deque will be blocked until space is available or elements are available.

2.1 LinkedTransferQueue

A TransferQueue is a BlockingQueue in which producers may wait for consumers to receive elements.

A LinkedTransferQueue is an unbounded TransferQueue based on linked nodes.

This queue orders elements FIFO (first-in-first-out) with respect to any given producer.

The head of the queue is the element that has been on the queue for the longest time for some producer.

The tail of the queue is the element that has been on the queue for the shortest time for some producer.

import java.time.Instant;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;

public class LinkedTransferQueueTest {
    public static void main(String[] args) throws InterruptedException {
        LinkedTransferQueue<String> queue = new LinkedTransferQueue<>();

        Runnable producer = () -> {
            String greeting = "hello world";
            System.out.println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : transfering");
            try {
                queue.transfer(greeting);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : transfering "
                    + greeting + " done");
        };

        Runnable consumer = () -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                String greeting = queue.take();
                System.out.println(
                        "[" + Instant.now() + "] " + Thread.currentThread().getName() + " : taking " + greeting
                                + " done");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread p1 = new Thread(producer);
        Thread c1 = new Thread(consumer);

        p1.start();
        c1.start();

        p1.join();
        c1.join();
    }
}

Below is the output of above code snippet :

[2025-04-17T19:16:17.080825245Z] Thread-0 : transfering
[2025-04-17T19:16:20.082303554Z] Thread-1 : taking hello world done
[2025-04-17T19:16:20.082234928Z] Thread-0 : transfering hello world done

2.2 ArrayBlockingQueue

The ArrayBlockingQueue class is a bounded blocking queue backed by an array.

By bounded, it means that the size of the Queue is fixed. Once created, the capacity cannot be changed.

Attempts to put an element into a full queue will result in the operation blocking.

Similarly attempts to take an element from an empty queue will also be blocked.

This queue orders elements FIFO (first-in-first-out).

It means that the head of this queue is the oldest element among the elements present in this queue.

The tail of this queue is the newest element among the elements of this queue.

import java.time.Instant;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ArrayBlockingQueueTest {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1);

        int count = 5;

        AtomicInteger producerWait = new AtomicInteger(1);
        AtomicInteger consumerWait = new AtomicInteger(2);

        Runnable producer = () -> {
            for (int i = 1; i <= count; i++) {
                try {
                    if (producerWait.get() <= consumerWait.get()) {
                        System.out
                                .println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : putting");
                    }
                    TimeUnit.SECONDS.sleep(producerWait.get());
                    queue.put(i);
                    if (producerWait.get() <= consumerWait.get()) {
                        System.out.println(
                                "[" + Instant.now() + "] " + Thread.currentThread().getName() + " : putting " + i
                                        + " done");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Runnable consumer = () -> {
            for (int i = 1; i <= count; i++) {
                try {
                    if (consumerWait.get() <= producerWait.get()) {
                        System.out
                                .println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : taking");
                    }
                    TimeUnit.SECONDS.sleep(consumerWait.get());
                    int taken = queue.take();
                    if (consumerWait.get() <= producerWait.get()) {
                        System.out.println(
                                "[" + Instant.now() + "] " + Thread.currentThread().getName() + " : taking "
                                        + taken
                                        + " done");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread p1 = new Thread(producer);
        Thread c1 = new Thread(consumer);

        p1.start();
        c1.start();

        p1.join();
        c1.join();

        System.out.println();

        producerWait.set(2);
        consumerWait.set(1);

        Thread p2 = new Thread(producer);
        Thread c2 = new Thread(consumer);

        p2.start();
        c2.start();

        c2.join();
        p2.join();
    }
}

Below is the output of above code snippet :

[2025-04-18T19:43:36.641236633Z] Thread-0 : putting
[2025-04-18T19:43:37.685372769Z] Thread-0 : putting 1 done
[2025-04-18T19:43:37.703707389Z] Thread-0 : putting
[2025-04-18T19:43:38.704403874Z] Thread-0 : putting 2 done
[2025-04-18T19:43:38.705122370Z] Thread-0 : putting
[2025-04-18T19:43:40.643226330Z] Thread-0 : putting 3 done
[2025-04-18T19:43:40.643631378Z] Thread-0 : putting
[2025-04-18T19:43:42.643870469Z] Thread-0 : putting 4 done
[2025-04-18T19:43:42.644179417Z] Thread-0 : putting
[2025-04-18T19:43:44.644734031Z] Thread-0 : putting 5 done

[2025-04-18T19:43:46.646194426Z] Thread-3 : taking
[2025-04-18T19:43:48.646776225Z] Thread-3 : taking 1 done
[2025-04-18T19:43:48.647853758Z] Thread-3 : taking
[2025-04-18T19:43:50.647335288Z] Thread-3 : taking 2 done
[2025-04-18T19:43:50.647625656Z] Thread-3 : taking
[2025-04-18T19:43:52.648028862Z] Thread-3 : taking 3 done
[2025-04-18T19:43:52.648337960Z] Thread-3 : taking
[2025-04-18T19:43:54.648654123Z] Thread-3 : taking 4 done
[2025-04-18T19:43:54.649070711Z] Thread-3 : taking
[2025-04-18T19:43:56.650299632Z] Thread-3 : taking 5 done

2.3 DelayQueue

The DelayQueue class is a blocking queue which allows an item to be held until a delay expires.

Elements inserted into a DelayQueue must implement the java.util.concurrent.Delayed interface which defines only one method.

MethodRole
long getDelay(TimeUnit timeUnit)Return the time to wait before the item is removed from the queue

If the value returned by the getDelay() method is negative or zero then the delay is considered to have expired and the element can be removed immediately.

The TimeUnit type parameter is used to specify the time unit in which the value must be returned.

Each element must also redefine the compareTo() method which is used by the DelayQueue to determine the order of returning an element.

The size of a DelayQueue cannot be limited, so adding a new element using the put() method is therefore never blocking.

On the other hand, the invocation of the take() method is blocking as long as the waiting period of an element is not reached.

If several elements reach their timeout during the invocation of the take() method, then this method returns the element whose timeout has been exceeded the most.

import java.time.Instant;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayQueueTest {
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<DelayedTask> delayQueue = new DelayQueue<>();

        delayQueue.put(new DelayedTask("Task 1", 5, TimeUnit.SECONDS));
        delayQueue.put(new DelayedTask("Task 2", 10, TimeUnit.SECONDS));
        delayQueue.put(new DelayedTask("Task 3", 15, TimeUnit.SECONDS));

        System.out.println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : start");
        while (!delayQueue.isEmpty()) {
            System.out.println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : taking ...");
            DelayedTask task = delayQueue.take();
            System.out.println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : "
                    + task.getName() + " taken");
        }
    }

    static class DelayedTask implements Delayed {
        private final String name;
        private final long endTime;

        public DelayedTask(String name, long delay, TimeUnit unit) {
            this.name = name;
            this.endTime = System.currentTimeMillis() + unit.toMillis(delay);
        }

        public String getName() {
            return name;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            long remainingTime = endTime - System.currentTimeMillis();
            return unit.convert(remainingTime, TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed other) {
            return Long.compare(this.endTime, ((DelayedTask) other).endTime);
        }
    }
}

The output of above code snippet is below :

[2025-04-19T06:33:17.763688591Z] main : start
[2025-04-19T06:33:17.805872721Z] main : taking ...
[2025-04-19T06:33:22.763343516Z] main : Task 1 taken
[2025-04-19T06:33:22.776245394Z] main : taking ...
[2025-04-19T06:33:27.763771485Z] main : Task 2 taken
[2025-04-19T06:33:27.764193362Z] main : taking ...
[2025-04-19T06:33:32.764028414Z] main : Task 3 taken

2.4 LinkedBlockingQueue

The LinkedBlockingQueue class implements the BlockingQueue interface using a LinkedList to store items internally.

It is possible to specify a maximum size for the number of elements that the queue can contain: by default, the maximum capacity of a LinkedBlockingQueue is set to the value Integer.MAX_VALUE unless a different value is provided as a constructor parameter.

In a LinkedBlockingQueue, the elements are stored in FIFO (First In, First Out) mode : the first element in the list is the one that has been in the queue for the longest time and the last item in the list is the one that arrived most recently in the queue.

The advantage of a LinkedBlockingQueue over an ArrayBlockingQueue is that you don’t have to limit the size of the collection : the producers are not blocked while waiting for the queue to empty. However, this can lead to other difficulties such as a lack of memory if the collection fills up much faster than it empties.

A LinkedBlockingQueue generally offers better performance than an ArrayBlockingQueue, especially if the queue must contain many objects, but it requires more objects in memory.

import java.time.Instant;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class LinkedBlockingQueueTest {
    public static void main(String[] args) throws InterruptedException {
        LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        // LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(1);
        int count = 5;

        AtomicInteger producerWait = new AtomicInteger(1);
        AtomicInteger consumerWait = new AtomicInteger(2);

        Runnable producer = () -> {
            for (int i = 1; i <= count; i++) {
                try {
                    if (producerWait.get() <= consumerWait.get()) {
                        System.out
                                .println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : putting");
                    }
                    TimeUnit.SECONDS.sleep(producerWait.get());
                    queue.put(i);
                    if (producerWait.get() <= consumerWait.get()) {
                        System.out.println(
                                "[" + Instant.now() + "] " + Thread.currentThread().getName() + " : putting " + i
                                        + " done");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Runnable consumer = () -> {
            for (int i = 1; i <= count; i++) {
                try {
                    if (consumerWait.get() <= producerWait.get()) {
                        System.out
                                .println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : taking");
                    }
                    TimeUnit.SECONDS.sleep(consumerWait.get());
                    int taken = queue.take();
                    if (consumerWait.get() <= producerWait.get()) {
                        System.out.println(
                                "[" + Instant.now() + "] " + Thread.currentThread().getName() + " : taking "
                                        + taken
                                        + " done");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread p1 = new Thread(producer);
        Thread c1 = new Thread(consumer);

        p1.start();
        c1.start();

        p1.join();
        c1.join();

        System.out.println();

        producerWait.set(2);
        consumerWait.set(1);

        Thread p2 = new Thread(producer);
        Thread c2 = new Thread(consumer);

        p2.start();
        c2.start();

        c2.join();
        p2.join();
    }
}

Below is the output of above code snippet :

[2025-04-20T05:27:10.018982373Z] Thread-0 : putting
[2025-04-20T05:27:11.066489563Z] Thread-0 : putting 1 done
[2025-04-20T05:27:11.080883402Z] Thread-0 : putting
[2025-04-20T05:27:12.081858449Z] Thread-0 : putting 2 done
[2025-04-20T05:27:12.082632098Z] Thread-0 : putting
[2025-04-20T05:27:13.083615873Z] Thread-0 : putting 3 done
[2025-04-20T05:27:13.084222452Z] Thread-0 : putting
[2025-04-20T05:27:14.084791820Z] Thread-0 : putting 4 done
[2025-04-20T05:27:14.085417400Z] Thread-0 : putting
[2025-04-20T05:27:15.086340891Z] Thread-0 : putting 5 done

[2025-04-20T05:27:20.024798297Z] Thread-3 : taking
[2025-04-20T05:27:22.025427007Z] Thread-3 : taking 1 done
[2025-04-20T05:27:22.027055696Z] Thread-3 : taking
[2025-04-20T05:27:24.026241048Z] Thread-3 : taking 2 done
[2025-04-20T05:27:24.026610168Z] Thread-3 : taking
[2025-04-20T05:27:26.027202178Z] Thread-3 : taking 3 done
[2025-04-20T05:27:26.028141307Z] Thread-3 : taking
[2025-04-20T05:27:28.027800354Z] Thread-3 : taking 4 done
[2025-04-20T05:27:28.028229664Z] Thread-3 : taking
[2025-04-20T05:27:30.028123937Z] Thread-3 : taking 5 done

2.5 SynchronousQueue

The SynchronousQueue class, added in Java 1.5, implements the BlockingQueue interface to provide an easy way to exchange an element between two threads and to synchronize these exchanges.

SynchronousQueue has no storage capacity: it can only contain one item at most.

Inserting a new element by invoking the put() method is blocked until the inserted element is removed.

Similarly, if the queue is empty then the invocation of the take() method will block until a new item is added to the collection.

import java.time.Instant;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynchronousQueueTest {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<String> queue = new SynchronousQueue<>();

        Runnable sender = () -> {
            try {
                String data = "Hello";
                logWithTimeStamp("Thread 1 sending data ...");
                queue.put(data);
                logWithTimeStamp("Thread 1 sent data : " + data);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Runnable receiver = () -> {
            try {
                logWithTimeStamp("Thread 2 receiving data ...");
                String data = queue.take();
                logWithTimeStamp("Thread 2 received data : " + data);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        int waiting = 5;

        Thread s1 = new Thread(sender);
        Thread r1 = new Thread(receiver);

        s1.start();
        TimeUnit.SECONDS.sleep(waiting);
        r1.start();

        s1.join();
        r1.join();

        System.out.println();

        Thread s2 = new Thread(sender);
        Thread r2 = new Thread(receiver);

        r2.start();
        TimeUnit.SECONDS.sleep(waiting);
        s2.start();

        s2.join();
        r2.join();
    }

    private static void logWithTimeStamp(String message) {
        System.out.println("[" + Instant.now() + "] " + Thread.currentThread().getName() + " : " + message);
    }
}

Below is the output of above code snippet :

[2025-04-21T06:58:59.427700974Z] Thread-0 : Thread 1 sending data ...
[2025-04-21T06:59:04.430417515Z] Thread-1 : Thread 2 receiving data ...
[2025-04-21T06:59:04.433949020Z] Thread-1 : Thread 2 received data : Hello
[2025-04-21T06:59:04.434053291Z] Thread-0 : Thread 1 sent data : Hello

[2025-04-21T06:59:04.435224778Z] Thread-3 : Thread 2 receiving data ...
[2025-04-21T06:59:09.436543323Z] Thread-2 : Thread 1 sending data ...
[2025-04-21T06:59:09.437154002Z] Thread-2 : Thread 1 sent data : Hello
[2025-04-21T06:59:09.437205290Z] Thread-3 : Thread 2 received data : Hello

2.6 PriorityQueue

The PriorityQueue class, added in Java 1.5, inherits from the AbstractQueue class.

The elements of the queue are ordered either by their natural order or by the use of an instance of the Comparator type provided as a parameter of the constructor.

If the queue uses the natural order then all the elements it contains must implement the Comparable interface otherwise an exception of type ClassCastException is thrown.

The size of a PriorityQueue is not restricted but it has an initial internal capacity which corresponds to the size of the array in which the elements will be stored.

The size of this array can evolve according to the number of elements contained in the queue.

Obtaining an element is done in the order in which the queue manages its elements : either by using the natural order of objects (by implementing the Comparable interface) or by using the Comparator type instance provided to the constructor who created the instance of the queue.

A collection of type PriorityQueue accepts having duplicates or elements that have the same priority. There is no guarantee on the order of obtaining these elements : the only solution is to be sufficiently discriminating in the comparison algorithm used by the queue to determine the order of the elements.

The PriorityQueue class is not thread-safe. If several threads must be able to modify this collection, the PriorityBlockingQueue class must be used.

import java.util.Collections;
import java.util.PriorityQueue;

public class PriorityQueueTest {
    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();

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

        System.out.println("Queue contents: " + queue);

        while (!queue.isEmpty()) {
            int element = queue.poll();
            System.out.println(element);
        }

        System.out.println();

        queue = new PriorityQueue<>(Collections.reverseOrder());

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

        System.out.println("Queue contents: " + queue);

        while (!queue.isEmpty()) {
            int element = queue.poll();
            System.out.println(element);
        }
    }
}

The output of above code snippet is below :

Queue contents: [1, 2, 3, 5, 4]
1
2
3
4
5

Queue contents: [5, 4, 3, 1, 2]
5
4
3
2
1

2.7 PriorityBlockingQueue

The PriorityBlockingQueue class is a Blocking Queue that returns the highest priority items first.

By default, the maximum capacity of a PriorityBlockingQueue is set to Integer.MAX_VALUE unless a different value is provided as a constructor parameter.

The PriorityBlockingQueue class stores its elements in an ordered manner using an instance of Comparator provided as a parameter of the constructor or using the method of the Comparable interface implemented by the elements of the collection.

If no instance of Comparator is provided as a constructor parameter, then the elements added to the queue must implement the Comparable interface, otherwise a ClassCastException exception is thrown.

The PriorityBlockingQueue class does not impose anything concerning elements which have the same priority : it is the implementation of the compareTo() method which can handle this case as needed.

import java.util.concurrent.PriorityBlockingQueue;

public class PriorityBlockingQueueTest {
    public static void main(String[] args) {
        PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>();

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

        while (true) {
            try {
                System.out.println(Thread.currentThread().getName() + " : taking from queue ...");
                int element = queue.take();
                System.out.println(element);
                System.out.println();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Below is the output of above code snippet :

main : taking from queue ...
1

main : taking from queue ...
2

main : taking from queue ...
3

main : taking from queue ...

2.8 ArrayDeque

The ArrayDeque class, added in Java 6, is an implementation of the Deque interface in the form of an array whose dimensions can evolve.

The ArrayDeque class is the implementation of choice for a FIFO-like queue.

The performance when adding an element to the beginning or end of the collection remains constant.

The ArrayDeque class has several characteristics :

  • It is not thread-safe: it does not offer any concurrent access management;
  • It imposes no restriction on its size and can grow as needed;
  • It does not allow to add a null element.

The ArrayDeque class can replace the Stack class for use as a stack and the LinkedList class for use as a queue.

The Iterators obtained from the collection are of the fail-fast type: if a modification is made to the content of the collection during the traversal with the Iterator then an exception of the ConcurrentModificationException type can be thrown unless this modification is made with the Iterator’s remove() method.

import java.util.ArrayDeque;

public class ArrayDequeTest {
    public static void main(String[] args) {
        ArrayDeque<Integer> stack = new ArrayDeque<>();

        stack.push(1);
        stack.push(2);
        stack.push(3);

        System.out.println(stack);

        while (!stack.isEmpty()) {
            System.out.println(stack.pop());
        }

        System.out.println();

        ArrayDeque<Integer> queue = stack;

        queue.offer(1);
        queue.offer(2);
        queue.offer(3);

        System.out.println(queue);

        while (!queue.isEmpty()) {
            System.out.println(queue.poll());
        }
    }
}

Below is the output of above code snippet :

[3, 2, 1]
3
2
1

[1, 2, 3]
1
2
3

2.9 LinkedBlockingDeque

The LinkedBlockingDeque class in Java is a thread-safe and concurrent implementation of the Deque interface that uses a linked list to store its elements.

It is a Deque which blocks a thread if that thread tries to insert elements into a full deque or remove elements from an empty deque.

It implements the BlockingDeque interface and provides an optionally-bounded functionality based on linked nodes.

This optional boundedness is served by passing the required size in the constructor and helps in preventing memory wastage.

When unspecified, the capacity is by default taken as Integer.MAX_VALUE.

import java.util.concurrent.LinkedBlockingDeque;

public class LinkedBlockingDequeTest {
    public static void main(String[] args) throws InterruptedException {
        LinkedBlockingDeque<Integer> stack = new LinkedBlockingDeque<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);

        System.out.println(stack);

        System.out.println(stack.takeFirst());
        System.out.println(stack.takeFirst());
        System.out.println(stack.takeFirst());
        System.out.println(stack.takeFirst());

        System.out
                .println("Thie line will never be printed bcz the main thread is blocked for taking from empty stack");
    }
}

The output of above code snippet is below :

[3, 2, 1]
3
2
1

3. The choice of a queue implementation

Several implementations of the Queue and BlockingQueue type are particularly useful for managing exchanges of objects between threads, for example by implementing the producer/consumer design pattern.

The implementation of Queue for general use should be chosen based on its required functionalities :

  • The management of concurrent access or not;
  • The operations are blocking or not;
  • The size of the collection is bounded or not.

Several implementations of the Queue type can only be used for very specific cases :

  • If the collection must manage its elements in order of priority and the concurrent accesses do not need to be managed then it is necessary to use the class PriorityQueue;
  • If the collection must manage its elements in order of priority and the concurrent accesses need to be managed then it is necessary to use the class PriorityBlockingQueue;
  • If the collection needs to handle an element before an element is retractable then use the DelayQueue class;
  • The SynchronousQueue class is particularly useful for exchanging an object between two threads (it has no storage capacity).