Deep Dive into Java’s SynchronousQueue: How It Works and Its Implementation

This article provides a comprehensive analysis of Java's SynchronousQueue, detailing its unique zero-capacity behavior, differences from other BlockingQueue implementations, internal structures such as TransferQueue and TransferStack, fair and non‑fair modes, and the core transfer algorithm with code examples.

Programmer DD
Programmer DD
Programmer DD
Deep Dive into Java’s SynchronousQueue: How It Works and Its Implementation

SynchronousQueue Overview

SynchronousQueue is a special BlockingQueue with zero capacity, meaning each put operation must wait for a corresponding take operation. It differs from other BlockingQueues because it does not store elements and methods like peek, contains, clear, and isEmpty are effectively no‑ops.

Key Characteristics

SynchronousQueue has no capacity; put and take must occur simultaneously.

Methods like peek, contains, clear, and isEmpty are ineffective.

Supports both fair (FIFO) and non‑fair (LIFO) ordering.

Internally uses TransferQueue for fair mode and TransferStack for non‑fair mode.

Implementation Details

Class Definition

public class SynchronousQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {

Constructors

public SynchronousQueue() { this(false); }
public SynchronousQueue(boolean fair) { this.fair = fair; }

TransferQueue (Fair Mode)

static final class TransferQueue<E> extends Transferer<E> { ... }

TransferQueue maintains a dummy node, head, tail, and uses CAS operations to enqueue and match nodes. The transfer method handles both put (data) and take (request) operations, advancing the tail and head as needed.

TransferStack (Non‑Fair Mode)

static final class TransferStack<E> extends Transferer<E> { ... }

TransferStack uses a stack of nodes with three states: REQUEST, DATA, and FULFILLING. Nodes are pushed and popped using CAS, and matching is performed by linking complementary nodes.

Transfer Method

The core transfer algorithm decides whether to enqueue a new node or match with an existing one, handling timeouts, interruptions, and cancellation. It works for both fair and non‑fair modes.

awaitFulfill

Object awaitFulfill(QNode s, E e, boolean timed, long nanos) { ... }

This method spins or blocks the thread until the node is matched, cancelled, timed out, or interrupted.

Cancellation

void tryCancel(Object cmp) { UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this); }

Cancellation marks a node as cancelled by setting its item field to itself.

Cleaning Up Nodes

void clean(QNode pred, QNode s) { ... }

The clean method removes cancelled nodes from the queue or stack, advancing head and tail pointers as necessary.

Conclusion

The SynchronousQueue implementation is complex, involving low‑level CAS operations, spin‑wait loops, and careful handling of fairness policies. Understanding its internal mechanisms helps developers appreciate the performance characteristics of concurrent data structures in Java.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyFairnessBlockingQueueSynchronousQueueTransferQueueTransferStack
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.