Usages of ArrayBlockingQueue vs LinkedBlockingQueue vs PriorityBlockingQueue vs DelayQueue in Java?

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

FeatureArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueueDelayQueue
Underlying StructureFixed-size arrayDoubly-linked listBinary heap (array-based)Priority queue (heap-based)
CapacityFixed (specified at creation)Optionally bounded (default unbounded)UnboundedUnbounded
OrderingFIFO (insertion order)FIFO (insertion order)Priority-based (natural or custom)Delay-based (earliest expiration first)
Blocking BehaviorBlocks when full/emptyBlocks when full (if bounded)/emptyBlocks when emptyBlocks when empty or delays not expired
Element RequirementAny objectAny objectComparable or ComparatorImplements Delayed
Use CaseFixed-size producer-consumerFlexible producer-consumerPriority task schedulingDelayed task scheduling

Detailed Comparison

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *