Differences between ArrayBlockingQueue, LinkedBlockingQue… — Cracked Java
// Java Collections Framework · BlockingQueue Family
SeniorTheoryBig TechGoogleAmazon

Differences between ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue, DelayQueue, PriorityBlockingQueue, LinkedTransferQueue.

Six implementations, each optimized for a different workload. The key axes are: bounded vs unbounded, single vs split lock, ordering policy, and whether the queue holds elements at all.

Comparison table

QueueCapacityLockingOrderingIteratorBest for
ArrayBlockingQueuebounded (fixed)1 ReentrantLock + 2 conditionsFIFOweakly consistentPredictable, bounded pipelines
LinkedBlockingQueueoptional bound (default MAX_VALUE)2 locks: takeLock, putLockFIFOweakly consistentHigh-throughput producer/consumer
SynchronousQueue0 — no storagelock-free handoff (TransferStack/TransferQueue)direct handoffalways emptyPure handoff, newCachedThreadPool
PriorityBlockingQueueunbounded (grows)1 ReentrantLockby Comparatorweakly consistentPriority work scheduling
DelayQueueunbounded1 ReentrantLockby getDelay()weakly consistentScheduled tasks, expiring caches
LinkedTransferQueueunboundedlock-free (CAS)FIFO + transfer()weakly consistentLow-latency handoff, mixed workloads

Details

ArrayBlockingQueue

A circular array with head/tail indices, one ReentrantLock, and two Conditions (notEmpty, notFull). Capacity is fixed at construction. Single-lock means put and take serialize against each other — fine for moderate throughput, predictable memory footprint.

BlockingQueue<Task> q = new ArrayBlockingQueue<>(1024);

LinkedBlockingQueue

Linked nodes with separate locks for head (take) and tail (put). Producers and consumers can proceed truly in parallel — much higher throughput than ArrayBlockingQueue under heavy load. Default capacity is Integer.MAX_VALUEbound it explicitly in production.

BlockingQueue<Task> q = new LinkedBlockingQueue<>(1024);  // BOUND IT

SynchronousQueue

Zero capacity. Every put blocks until a matching take, and vice versa — it's a rendezvous, not a queue. Used by Executors.newCachedThreadPool(): if no thread is currently idle and available to grab the task, the executor creates a new one. There is literally nowhere to "queue" tasks.

SynchronousQueue<Job> q = new SynchronousQueue<>();
// producer: q.put(j);   // blocks until a consumer is calling q.take()

Fair (new SynchronousQueue<>(true)) gives FIFO matching of waiters; unfair (the default) uses a stack — better throughput, possible starvation.

PriorityBlockingQueue

Binary heap, unbounded (grows). Elements are dequeued in priority order per a Comparator (or natural ordering). Iterator is not in priority order — only poll/take honor priority.

PriorityBlockingQueue<Job> q = new PriorityBlockingQueue<>(64,
    Comparator.comparingInt(Job::priority));

Note: no blocking on put (it's unbounded), but take blocks when empty.

DelayQueue

Heap of Delayed elements. take() only returns elements whose getDelay(...) ≤ 0 — others stay in the queue. Used internally by ScheduledThreadPoolExecutor. Element type must implement Delayed.

class TimedJob implements Delayed {
    private final long readyAt;       // nanos
    public long getDelay(TimeUnit u) { return u.convert(readyAt - System.nanoTime(), NANOSECONDS); }
    public int compareTo(Delayed o) { ... }
}

LinkedTransferQueue

Lock-free (uses CAS heavily), unbounded, FIFO. Adds transfer(e) — block until a consumer takes the element, like SynchronousQueue.put, but the queue can also hold elements when no consumer is waiting. tryTransfer(e) is the non-blocking variant. Often the highest-throughput option on modern multi-core hardware.

LinkedTransferQueue<Event> q = new LinkedTransferQueue<>();
q.transfer(e);   // blocks until taken — handoff
q.put(e);        // enqueues — does not block

Quick chooser

Need priorities?           -> PriorityBlockingQueue
Need scheduling/delays?    -> DelayQueue
Need pure handoff?         -> SynchronousQueue (or LinkedTransferQueue.transfer)
Need lock-free low latency?-> LinkedTransferQueue
General-purpose, bounded?  -> ArrayBlockingQueue or LinkedBlockingQueue
   - tight memory bound?   -> ArrayBlockingQueue
   - highest throughput?   -> LinkedBlockingQueue (bounded!)

Mark your status