Which concurrent Queue implementation should I use in Java?
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Choosing the right concurrent queue implementation in Java is crucial for building efficient and thread-safe applications. Java provides several implementations with different characteristics and performance implications. This article delves into these options, providing technical insights and practical examples.
Understanding Concurrent Queues
Concurrent queues in Java are part of the java.util.concurrent package, which provides thread-safe queue implementations. These queues allow multiple threads to add and remove elements concurrently without external synchronization.
Types of Concurrent Queues in Java
Java provides several key concurrent queue implementations, each designed for specific use cases:
ConcurrentLinkedQueueLinkedBlockingQueueArrayBlockingQueuePriorityBlockingQueueSynchronousQueue
1. ConcurrentLinkedQueue
ConcurrentLinkedQueue is an unbounded, non-blocking, thread-safe queue based on linked nodes. It implements a classic lock-free algorithm, which enables high throughput in concurrent applications.
Use Cases:
- Non-blocking operations are crucial.
- High-performance, low-latency applications.
- Scenarios where you can tolerate potentially unbounded queues.
Example:
2. LinkedBlockingQueue
LinkedBlockingQueue is a bounded (or optionally unbounded) blocking queue backed by linked nodes. It supports operations that wait for the queue to become non-empty or for space to become available.
Use Cases:
- Need blocking operations (e.g., producer-consumer scenarios).
- Situations requiring a bounded capacity to prevent excessive memory consumption.
Example:
3. ArrayBlockingQueue
ArrayBlockingQueue is a bounded blocking queue backed by an array. It is particularly suitable when you require bounded blocking functionality without the overhead of node-based structures.
Use Cases:
- Bounded capacity is essential.
- Array-based structure preferred for consistent performance.
Example:
4. PriorityBlockingQueue
PriorityBlockingQueue is an unbounded blocking queue that orders elements based on their natural ordering or by a comparator. It does not block when adding new elements.
Use Cases:
- Need for elements to be ordered based on priority.
- Complex data processing pipelines that require prioritized task execution.
Example:
5. SynchronousQueue
SynchronousQueue is a blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa. It does not have any capacity and relies on handoff for concurrent task transfer.
Use Cases:
- Direct hand-off design patterns, where element transfer is immediate between producers and consumers.
- Zero-capacity scenario requiring immediate node transfer.
Example:
Comparison Table
| Queue Type | Blocking | Bounded | Characteristics | Use Cases |
| ConcurrentLinkedQueue | No | No | Non-blocking, lock-free | High-performance applications Unbounded scenarios |
| LinkedBlockingQueue | Yes | Yes | Blocking operations, node-based structure | Producer-consumer scenarios Memory management |
| ArrayBlockingQueue | Yes | Yes | Bounded, array-based structure | Consistent performance Fixed capacity requirements |
| PriorityBlockingQueue | Yes | No | Priority ordering, blocking retrieval | Prioritized tasks Complex processing pipelines |
| SynchronousQueue | Yes | No | Zero capacity, immediate handoff, direct exchange | Handoff protocols Direct producer-consumer interaction |
Conclusion
Choosing the right concurrent queue depends on specific application requirements, such as the need for bounded storage, blocking vs. non-blocking operations, and performance constraints. For non-blocking, high-throughput requirements, ConcurrentLinkedQueue is ideal. In contrast, if blocking is essential, LinkedBlockingQueue or ArrayBlockingQueue would be more suitable. For scenarios needing priority-based processing, PriorityBlockingQueue is the preferred choice, and for direct hand-off protocols, SynchronousQueue is recommended. Understanding these characteristics ensures optimal performance and scalability in Java's concurrent applications.
Related reading
- Which data structures and algorithms book should I buy?
- Which data structures to use when storing multiple entities with multiple query criteria?
- Which datatype to use as queue in Dijkstra's algorithm?
- which design considerations justify stdmake_heap to be apparently sub-optimal?
- which flood-fill algorithm is better for performance?
- Which is better in python, del or delattr?
- Which io_context does stdboostasiopost / dispatch use?
- Which is more efficient - Task.Run with 2 awaited I/O bound Tasks, or classic Fork/Join approach?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.