Skip to main content
  1. Java Concurrency (java.util.concurrent)/

LinkedTransferQueue

2 mins

LinkedTransferQueue is arguably the most advanced BlockingQueue in the JDK. It implements the TransferQueue interface, which provides a middle ground between LinkedBlockingQueue and SynchronousQueue.

Source Code #

View Source on GitHub

The “Dual Queue” Algorithm #

LinkedTransferQueue uses a “dual queue” algorithm (Scherer & Scott). A dual queue is a single linked structure that can hold either data nodes (provided by producers) or request nodes (provided by consumers).

public interface TransferQueue<E> extends BlockingQueue<E> {
    // Transfers the element to a consumer, blocking if necessary
    void transfer(E e) throws InterruptedException;

    // Tries to transfer immediately, returns false if no consumer waiting
    boolean tryTransfer(E e);

    // Tries to transfer, blocks for a specific time if needed
    boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException;

    // Returns true if there is at least one waiting consumer
    boolean hasWaitingConsumer();
}

Unlike LinkedBlockingQueue, which uses ReentrantLock, LinkedTransferQueue is designed to be mostly wait-free using CAS (Compare-And-Swap) operations for better scalability on modern multi-core processors.

Canonical Usage #

When to use: Use LinkedTransferQueue when you need a high-throughput, unbounded queue that can optionally act as a direct handoff (transfer()). It is a general-purpose replacement for LinkedBlockingQueue in performance-critical applications.

Common Patterns:

  • Hybrid Producer-Consumer: Use put() for standard asynchronous queuing and transfer() when you need to be sure a consumer has actually started processing the item before the producer continues.
  • Direct Request Fulfillment: It can fulfill a consumer’s request immediately if a producer is already waiting, or vice versa.
// Unbounded high-performance queue
TransferQueue<String> queue = new LinkedTransferQueue<>();

// Traditional put (producer proceeds immediately)
queue.put("Asynchronous Task");

// Transfer (producer blocks until a consumer takes it)
queue.transfer("Critical Synchronous Task");

Performance Trade-offs #

  • Pros: Scales exceptionally well with thread count. Combines the best of LinkedBlockingQueue and SynchronousQueue. Minimal locking.
  • Cons: Every insertion allocates a Node. Unbounded, so it must be used carefully to avoid OutOfMemoryError.