Unlocking LinkedBlockingDeque: How Java’s Double‑Ended Blocking Queue Works Under the Hood
This article explains the internal design of Java's LinkedBlockingDeque, a double‑ended blocking queue built on a linked list, covering its class hierarchy, core fields, insertion and removal methods, and the lock‑based concurrency mechanisms that enable FIFO and FILO operations.
LinkedBlockingDeque is a double‑ended blocking queue based on a linked list, supporting both FIFO and FILO operations.
Class Overview
LinkedBlockingDeque extends AbstractQueue and implements BlockingDeque , Serializable. It uses a ReentrantLock and two Conditions (notEmpty, notFull) to coordinate producers and consumers.
public class LinkedBlockingDeque<E> extends AbstractQueue<E>
implements BlockingDeque<E>, java.io.Serializable {Internal node structure:
static final class Node<E> {
E item;
Node<E> prev;
Node<E> next;
Node(E x) { item = x; }
}Key fields:
transient Node<E> first;
transient Node<E> last;
private transient int count;
private final int capacity;
final ReentrantLock lock = new ReentrantLock();
final Condition notEmpty = lock.newCondition();
final Condition notFull = lock.newCondition();Insertion methods delegate to linkFirst or linkLast after acquiring the lock and waiting on notFull when the queue is full.
public void putFirst(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (!linkFirst(node))
notFull.await();
} finally {
lock.unlock();
}
} linkFirstlinks a new node at the head, updates pointers, increments count, and signals notEmpty.
private boolean linkFirst(Node<E> node) {
if (count >= capacity) return false;
Node<E> f = first;
node.next = f;
first = node;
if (last == null)
last = node;
else
f.prev = node;
++count;
notEmpty.signal();
return true;
}Similarly, putLast uses linkLast to add at the tail.
private boolean linkLast(Node<E> node) {
if (count >= capacity) return false;
Node<E> l = last;
node.prev = l;
last = node;
if (first == null)
first = node;
else
l.next = node;
++count;
notEmpty.signal();
return true;
}Removal methods pollFirst and pollLast acquire the lock, call unlinkFirst or unlinkLast, and signal notFull.
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
} finally {
lock.unlock();
}
} unlinkFirstdetaches the head node, updates pointers, decrements count, and signals notFull.
private E unlinkFirst() {
Node<E> f = first;
if (f == null) return null;
Node<E> n = f.next;
E item = f.item;
f.item = null;
f.next = f; // help GC
first = n;
if (n == null)
last = null;
else
n.prev = null;
--count;
notFull.signal();
return item;
} unlinkLastperforms the symmetric operation at the tail.
private E unlinkLast() {
Node<E> l = last;
if (l == null) return null;
Node<E> p = l.prev;
E item = l.item;
l.item = null;
l.prev = l; // help GC
last = p;
if (p == null)
first = null;
else
p.next = null;
--count;
notFull.signal();
return item;
}All primary operations of LinkedBlockingDeque are built on these four internal methods, making the double‑ended queue behavior clear.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
