In concurrent package, ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue, and DelayQueue are implementations of the BlockingQueue interface. Each serves distinct purposes based on their structure and behavior. Below is a detailed comparison with examples.
Key Differences
Feature | ArrayBlockingQueue | LinkedBlockingQueue | PriorityBlockingQueue | DelayQueue |
---|---|---|---|---|
Underlying Structure | Fixed-size array | Doubly-linked list | Binary heap (array-based) | Priority queue (heap-based) |
Capacity | Fixed (specified at creation) | Optionally bounded (default unbounded) | Unbounded | Unbounded |
Ordering | FIFO (insertion order) | FIFO (insertion order) | Priority-based (natural or custom) | Delay-based (earliest expiration first) |
Blocking Behavior | Blocks when full/empty | Blocks when full (if bounded)/empty | Blocks when empty | Blocks when empty or delays not expired |
Element Requirement | Any object | Any object | Comparable or Comparator | Implements Delayed |
Use Case | Fixed-size producer-consumer | Flexible producer-consumer | Priority task scheduling | Delayed task scheduling |
Detailed Comparison
- ArrayBlockingQueue
- Structure: Fixed-size array.
- Capacity: Must specify at creation (e.g., 10 elements).
- Ordering: FIFO (First-In-First-Out).
- Behavior: Blocks on put() when full and take() when empty.
- LinkedBlockingQueue
- Structure: Doubly-linked list.
- Capacity: Optionally bounded (defaults to Integer.MAX_VALUE if unbounded).
- Ordering: FIFO.
- Behavior: Blocks on put() when full (if bounded) and take() when empty.
- PriorityBlockingQueue
- Structure: Binary heap (array-based).
- Capacity: Unbounded (grows as needed).
- Ordering: Elements are ordered by natural order (e.g., Comparable) or a custom Comparator.
- Behavior: Blocks on take() when empty; no blocking on put() due to unbounded nature.
- DelayQueue
- Structure: Priority queue (heap-based).
- Capacity: Unbounded.
- Ordering: Elements are ordered by their delay expiration (earliest first).
- Behavior: Blocks on take() until an element’s delay expires; only accepts objects implementing Delayed.
Examples
1. ArrayBlockingQueue Example
java
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class ArrayBlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); // Fixed size: 2 Thread producer = new Thread(() -> { try { queue.put("A"); System.out.println("Produced: A"); queue.put("B"); System.out.println("Produced: B"); queue.put("C"); // Blocks until space is available System.out.println("Produced: C"); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread consumer = new Thread(() -> { try { Thread.sleep(2000); System.out.println("Consumed: " + queue.take()); System.out.println("Consumed: " + queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }); producer.start(); consumer.start(); } }
Output:
Produced: A Produced: B [Blocks for 2 seconds] Consumed: A Consumed: B Produced: C
2. LinkedBlockingQueue Example
java
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.BlockingQueue; public class LinkedBlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new LinkedBlockingQueue<>(2); // Bounded to 2 Thread producer = new Thread(() -> { try { queue.put("X"); System.out.println("Produced: X"); queue.put("Y"); System.out.println("Produced: Y"); queue.put("Z"); // Blocks if bounded System.out.println("Produced: Z"); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread consumer = new Thread(() -> { try { Thread.sleep(2000); System.out.println("Consumed: " + queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }); producer.start(); consumer.start(); } }
Output (Bounded):
Produced: X Produced: Y [Blocks for 2 seconds] Consumed: X Produced: Z
- Unbounded (new LinkedBlockingQueue<>()) would not block on put().
3. PriorityBlockingQueue Example
java
import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.BlockingQueue; public class PriorityBlockingQueueExample { public static void main(String[] args) { BlockingQueue<Integer> queue = new PriorityBlockingQueue<>(); // Unbounded, priority-based Thread producer = new Thread(() -> { try { queue.put(3); queue.put(1); queue.put(2); System.out.println("Produced: 3, 1, 2"); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread consumer = new Thread(() -> { try { System.out.println("Consumed: " + queue.take()); // Smallest first System.out.println("Consumed: " + queue.take()); System.out.println("Consumed: " + queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }); producer.start(); consumer.start(); } }
Output:
Produced: 3, 1, 2 Consumed: 1 Consumed: 2 Consumed: 3
4. DelayQueue Example
java
import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; public class DelayQueueExample { static class DelayedTask implements Delayed { private final String name; private final long expiryTime; DelayedTask(String name, long delayMs) { this.name = name; this.expiryTime = System.currentTimeMillis() + delayMs; } @Override public long getDelay(TimeUnit unit) { return unit.convert(expiryTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { return Long.compare(this.expiryTime, ((DelayedTask) o).expiryTime); } @Override public String toString() { return name; } } public static void main(String[] args) { DelayQueue<DelayedTask> queue = new DelayQueue<>(); Thread producer = new Thread(() -> { queue.put(new DelayedTask("Task 1", 2000)); queue.put(new DelayedTask("Task 2", 1000)); System.out.println("Produced tasks with delays"); }); Thread consumer = new Thread(() -> { try { System.out.println("Consumed: " + queue.take()); // Waits for delay System.out.println("Consumed: " + queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }); producer.start(); consumer.start(); } }
Output:
Produced tasks with delays [Waits 1 second] Consumed: Task 2 [Waits 1 more second] Consumed: Task 1
Summary
- ArrayBlockingQueue: Fixed-size, FIFO, best for bounded producer-consumer.
- LinkedBlockingQueue: Flexible capacity, FIFO, good for dynamic workloads.
- PriorityBlockingQueue: Unbounded, priority-based, ideal for task prioritization.
- DelayQueue: Unbounded, delay-based, perfect for scheduled tasks.