Producer-Consumer problem in Java
Published on August 7, 2024
Example Producer-Consumer problem in Java using a shared buffer and basic synchronization:
The Producer-Consumer problem involves multiple threads: producers adding data items to a shared buffer and consumers removing them. The challenge is to synchronize their access to the buffer, ensuring producers wait when it's full and consumers wait when it's empty, while preventing race conditions and ensuring efficient thread communication
import java.util.LinkedList;
import java.util.Queue;
class SharedBuffer {
private Queue<Integer> buffer = new LinkedList<>();
private int capacity;
public SharedBuffer(int capacity) {
this.capacity = capacity;
}
public synchronized void produce(int item) throws InterruptedException {
while (buffer.size() == capacity) {
wait(); // Wait if buffer is full
}
buffer.offer(item);
System.out.println("Produced: " + item);
notify(); // Notify consumer that new item is produced
}
public synchronized int consume() throws InterruptedException {
while (buffer.isEmpty()) {
wait(); // Wait if buffer is empty
}
int item = buffer.poll();
System.out.println("Consumed: " + item);
notify(); // Notify producer that an item is consumed
return item;
}
}
class Producer extends Thread {
private SharedBuffer buffer;
public Producer(SharedBuffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
try {
buffer.produce(i);
Thread.sleep((int) (Math.random() * 100)); // Simulate some work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
class Consumer extends Thread {
private SharedBuffer buffer;
public Consumer(SharedBuffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
try {
int item = buffer.consume();
Thread.sleep((int) (Math.random() * 100)); // Simulate some work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public class Main {
public static void main(String[] args) {
SharedBuffer buffer = new SharedBuffer(3); // Buffer capacity of 3
Producer producer = new Producer(buffer);
Consumer consumer = new Consumer(buffer);
producer.start();
consumer.start();
}
}
Explanation:
-
SharedBuffer: Manages a shared buffer (
Queue<Integer> buffer
) with a specifiedcapacity
. It provides synchronized methodsproduce(int item)
andconsume()
to add items to and remove items from the buffer respectively. It useswait()
andnotify()
for synchronization. -
Producer: Extends
Thread
and produces (adds) items to the shared buffer usingproduce(int item)
method ofSharedBuffer
. -
Consumer: Also extends
Thread
and consumes (removes) items from the shared buffer usingconsume()
method ofSharedBuffer
. -
Main: Creates an instance of
SharedBuffer
with capacity 3, and instances ofProducer
andConsumer
. Starts their threads to run concurrently.
Key Points:
-
The
produce()
method checks if the buffer is full (buffer.size() == capacity
) and waits (wait()
) if true. Once an item is produced, it adds (offer()
) it to the buffer and notifies (notify()
) the consumer. -
The
consume()
method checks if the buffer is empty (buffer.isEmpty()
) and waits (wait()
) if true. Once an item is consumed, it removes (poll()
) it from the buffer and notifies (notify()
) the producer. -
wait()
andnotify()
methods are used for synchronization to ensure that producers and consumers wait appropriately when the buffer is full or empty, and to avoid race conditions.