Business Consumer Problem in Java
In concurrent programming, the business consumer problem is a classic example of a synchronization problem where multiple threads are involved in a shared resource. The producer thread generates the data, and the consumer thread consumes it. The solution to this problem requires careful coordination and synchronization between the producer and consumer threads to prevent race conditions and ensure that the data is produced and consumed in a timely and orderly manner.
Java provides built-in support for multithreading through the java.util.concurrent package. In this article, we will explore how to implement the producer-consumer solution using threads in Java.
Problem Statement
Before diving into the solution, let's understand the problem in detail. Consider a scenario where a producer thread generates data, and a consumer thread consumes it. The producer thread must wait until the consumer thread consumes the previous data before generating new data, and the consumer thread must wait until the producer thread generates new data before consuming it. This scenario can be visualized as a circular buffer where the producer thread writes to the buffer, and the consumer thread reads from it.
The challenge in this scenario is to ensure that the producer and consumer threads do not access the buffer simultaneously, leading to data corruption. We need to synchronize the access to the buffer and ensure that the producer and consumer threads take turns accessing it.
Approach
The solution to the business consumer problem using threads in Java involves creating two classes, the Producer class, and the Consumer class. The Producer class generates the data, and the Consumer class consumes it.
Algorithm:
- Create a shared buffer as a LinkedList.
- Create a Producer class that implements the Runnable interface.
- Create a Consumer class that implements the Runnable interface.
- In the Producer class, in the run method, use a synchronized block to: Wait if the buffer is full using buffer.wait(). Produce an item and add it to the buffer using buffer.add(item).
- Notify the Consumer thread that an item is available using buffer.notify().
- In the Consumer class, in the run method, use a synchronized block to: Wait if the buffer is empty using buffer.wait().
- Retrieve the first item from the buffer using buffer.removeFirst(). Notify the Producer thread that space is available in the buffer using buffer.notify().
- In the main method, create instances of the Producer and Consumer classes and start them using the Thread class.
Here is the code for the Producer class:
Businessconsumer.java
import java.util.LinkedList;
public class Businessconsumer {
public static void main(String[] args) {
// Create shared buffer
LinkedList<Integer> buffer = new LinkedList<>();
// Create Producer and Consumer threads
Thread producer = new Thread(new Producer(buffer));
Thread consumer = new Thread(new Consumer(buffer));
// Start the threads
producer.start();
consumer.start();
}
}
class Producer implements Runnable {
private LinkedList<Integer> buffer;
private final int MAX_SIZE = 10;
public Producer(LinkedList<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
synchronized (buffer) {
// Wait if buffer is full
while (buffer.size() == MAX_SIZE) {
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Produce item and add to buffer
int item = (int) (Math.random() * 100);
buffer.add(item);
System.out.println("Produced item: " + item);
// Notify consumer that item is available
buffer.notify();
}
}
}
}
class Consumer implements Runnable {
private LinkedList<Integer> buffer;
public Consumer(LinkedList<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
synchronized (buffer) {
// Wait if buffer is empty
while (buffer.size() == 0) {
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Consume item from buffer
int item = buffer.removeFirst();
System.out.println("Consumed item: " + item);
// Notify producer that buffer is available
buffer.notify();
}
}
}
}
Output:
Produced item: 22
Consumed item: 22
Produced item: 97
Consumed item: 97
Produced item: 37
Consumed item: 37
Produced item: 8
Consumed item: 8
The output shows that the producer thread is producing items and adding them to the shared buffer, and the consumer thread is consuming the items and removing them from the buffer. The program will run indefinitely until it is manually stopped.
Complexity
The time complexity of the Producer-Consumer problem depends on the buffer size and the number of items produced or consumed. In the worst case, the Producer and Consumer threads can wait for each other indefinitely, leading to a deadlock. However, the complexity of the Producer-Consumer problem using a shared buffer is typically O(1) for adding or retrieving items from the buffer, assuming that the buffer is implemented efficiently.