Java ArrayBlockingQueue
In multi-threaded programming, managing shared resources efficiently is a common challenge. Java's ArrayBlockingQueue
class is a useful tool for managing a thread-safe queue with a fixed capacity. It is part of the java.util.concurrent
package and implements the BlockingQueue
interface, which provides blocking operations for inserting and removing elements from the queue.
An ArrayBlockingQueue
is particularly useful in scenarios where you need to control the flow of elements between threads (for example, in producer-consumer patterns) while ensuring that the queue doesn't exceed a set limit. It provides bounded queues, meaning there is a predefined capacity, and when full, the producer threads are blocked until space becomes available.
The ArrayBlockingQueue
class in Java is a type of BlockingQueue
that is backed by an array, offering a fixed-size queue for elements. The size is specified when the queue is created, and once the queue reaches its capacity, attempts to add elements will cause the producer thread to block until space is available. Similarly, when the queue is empty, consumer threads attempting to remove elements will block until data becomes available.
Key features:
ArrayBlockingQueue
are atomic and thread-safe.put()
and take()
operations, which allow for safe data exchange between threads.The constructor for ArrayBlockingQueue
requires a capacity value. You can also specify a fairness policy.
ArrayBlockingQueue(int capacity)
ArrayBlockingQueue(int capacity, boolean fair)
true
, threads will be granted access to the queue in the order they requested it (FIFO). If false
, the queue will not guarantee any particular order.Here are some of the common methods provided by the ArrayBlockingQueue
class:
false
if the queue is full.null
if the queue is empty.Here’s a simple example demonstrating how to use an ArrayBlockingQueue
with a producer-consumer scenario.
import java.util.concurrent.ArrayBlockingQueue;
public class Main {
public static void main(String[] args) throws InterruptedException {
// Create an ArrayBlockingQueue with a capacity of 2
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
// Producer thread
Thread producer = new Thread(() -> {
try {
// Add items to the queue
System.out.println("Producer: Adding item 1");
queue.put("Item 1");
System.out.println("Producer: Adding item 2");
queue.put("Item 2");
System.out.println("Producer: Adding item 3");
queue.put("Item 3"); // This will block as the queue is full
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// Consumer thread
Thread consumer = new Thread(() -> {
try {
// Remove items from the queue
System.out.println("Consumer: Removing " + queue.take());
System.out.println("Consumer: Removing " + queue.take());
System.out.println("Consumer: Removing " + queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
}
}
Output:
Producer: Adding item 1
Producer: Adding item 2
Consumer: Removing Item 1
Producer: Adding item 3
Consumer: Removing Item 2
Consumer: Removing Item 3
In this example:
You can use the fairness option when creating an ArrayBlockingQueue
. This ensures that threads are granted access to the queue in the order they requested it (FIFO).
import java.util.concurrent.ArrayBlockingQueue;
public class FairQueueExample {
public static void main(String[] args) throws InterruptedException {
// Create a fair ArrayBlockingQueue with a capacity of 2
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(2, true);
// Producer thread
Thread producer = new Thread(() -> {
try {
System.out.println("Producer: Adding item 1");
queue.put("Item 1");
System.out.println("Producer: Adding item 2");
queue.put("Item 2");
System.out.println("Producer: Adding item 3");
queue.put("Item 3"); // This will block as the queue is full
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// Consumer thread
Thread consumer1 = new Thread(() -> {
try {
System.out.println("Consumer 1: Removing " + queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer2 = new Thread(() -> {
try {
System.out.println("Consumer 2: Removing " + queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
consumer1.start();
consumer2.start();
producer.start();
producer.join();
consumer1.join();
consumer2.join();
}
}
Output:
Producer: Adding item 1
Producer: Adding item 2
Consumer 1: Removing Item 1
Producer: Adding item 3
Consumer 2: Removing Item 2
In this example, even if two consumer threads are waiting, the fair mode ensures that they are served in the order they were created.
The ArrayBlockingQueue
is ideal for the following use cases: