Implementing Two Java Blocking Queues for Thread Communication
Implementing Two Java Blocking Queues for Thread Communication
Overview
Java provides robust blocking queue implementations to manage thread synchronization and communication. This example uses ArrayBlockingQueue and LinkedBlockingQueue to demonstrate sequential data transfer between two threads.
Implementation Steps
- Initialize two distinct blocking queues
- Create two worker threads: one produces elements to the first queue, and another consumes those elements and transfers them to the second queue
- Leverage built-in blocking behavior of
putandtaketo handle synchronization automatically
Code Implementation
Step 1: Initialize Blocking Queues
// Fixed-capacity ArrayBlockingQueue with 15 slots
ArrayBlockingQueue<String> dataChannel1 = new ArrayBlockingQueue<>(15);
// Unbounded LinkedBlockingQueue
LinkedBlockingQueue<String> dataChannel2 = new LinkedBlockingQueue<>();
Step 2: Create Worker Threads
// Producer thread: sends data to dataChannel1
Thread dataProducer = new Thread(() -> {
try {
String message = "Processing Request #101";
dataChannel1.put(message);
System.out.println("Produced and sent to Channel 1: " + message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Producer thread interrupted");
}
});
// Consumer-Forwarder thread: retrieves from dataChannel1 and sends to dataChannel2
Thread dataTransporter = new Thread(() -> {
try {
String receivedMessage = dataChannel1.take();
System.out.println("Retrieved from Channel 1: " + receivedMessage);
dataChannel2.put(receivedMessage);
System.out.println("Forwarded to Channel 2: " + receivedMessage);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Transporter thread interrupted");
}
});
// Start both threads
dataProducer.start();
dataTransporter.start();
Blocking Mechanism Explanation
The put() method blocks if dataChannel1 reaches capacity, and take() blocks if dataChannel1 is empty, ensuring no race conditions or manual synchronization logic are needed.
Usage Scenario
This patteern is ideal for decoupling processing stages in multi-threaded aplications, such as pipeline architectures where raw data is first collected and then processed.
Key Differences Between the Two Queues
| Feature | ArrayBlockingQueue | LinkedBlockingQueue |
|---|---|---|
| Capacity | Fixed (must be specified) | Optional (unbounded by default) |
| Underlying Structure | Array-based (contiguous memory) | Doubly linked list (dynamic nodes) |
| Performance | Faster for bounded, predictable loads | Better for unbounded, variable loads |
| Locking Strategy | Single lock (for both put/take) | Separate locks for put and take |