Java BlockingQueue
In Java, BlockingQueue
is a key interface in the java.util.concurrent
package designed to support thread-safe operations for concurrent programming. It is particularly useful when you need to manage tasks or data between multiple threads, ensuring smooth coordination without race conditions.
The BlockingQueue
interface is ideal for situations where threads may be waiting for elements to become available or where resources must be managed efficiently. It provides various operations that allow threads to block until the desired condition is met, such as waiting for data to be available or waiting for space to free up.
The BlockingQueue
interface extends the Queue
interface and provides methods that allow threads to insert, remove, and examine elements while supporting blocking operations. It is a thread-safe collection designed to be used in a multi-threaded environment.
The core idea behind BlockingQueue
is that operations like insertions and removals can cause threads to wait (block) until certain conditions are met:
BlockingQueue
implementations are thread-safe and designed to work with multiple threads.take()
and put()
can block if necessary, making it ideal for producer-consumer scenarios.The BlockingQueue
interface provides several methods for inserting, removing, and examining elements:
false
if it can't be inserted immediately (for unbounded queues).null
if the queue is empty.BlockingQueue
that orders elements according to their natural ordering or a specified comparator.The ArrayBlockingQueue
is a bounded queue, meaning it has a fixed capacity. When full, it will block any threads attempting to add more elements until space becomes available.
import java.util.concurrent.ArrayBlockingQueue;
public class Main {
public static void main(String[] args) throws InterruptedException {
// Create a BlockingQueue with a capacity of 2
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
// 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 consumer = new Thread(() -> {
try {
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:
ArrayBlockingQueue
. When it attempts to add the third item, it blocks because the queue is full (capacity is 2).The LinkedBlockingQueue
is an implementation that can be either bounded or unbounded. Here, we will use an unbounded LinkedBlockingQueue
to show how a producer and consumer can operate concurrently.
import java.util.concurrent.LinkedBlockingQueue;
public class Main {
public static void main(String[] args) throws InterruptedException {
// Create an unbounded LinkedBlockingQueue
LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
// Producer thread
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
System.out.println("Producer: Adding item " + i);
queue.put("Item " + i); // This will block if the queue is full
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// Consumer thread
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
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
Consumer: Removing Item 1
Producer: Adding item 2
Consumer: Removing Item 2
Producer: Adding item 3
Consumer: Removing Item 3
Producer: Adding item 4
Consumer: Removing Item 4
Producer: Adding item 5
Consumer: Removing Item 5
In this case, the producer and consumer work in parallel, with the producer adding items to the LinkedBlockingQueue
and the consumer taking items from it.
BlockingQueue
is ideal for the following scenarios:
BlockingQueue
for task scheduling in multi-threaded applications, where workers (consumer threads) process tasks (producer threads) asynchronously.