Producer Consumer Problem in Java Using Synchronized Block
The producer-consumer dilemma is a well-known instance of a multi-process synchronization issue in computing. Two processes—the producer and the consumer—are described in the issue, and they share a single, fixed-size buffer that serves as a queue.
- The producer's responsibility is to produce data, store it in the buffer, and start over.
- The customer is simultaneously absorbing the info one piece at a time.
Problem Statement
To ensure that neither the producer nor the consumer would attempt to remove data from an empty buffer or add data to one already full.
Approach
If the buffer is full, the producer must either sleep or discard data. The producer restarts filling the buffer after receiving a notification from the consumer the next time an item is removed from the buffer. The consumer has the same option to sleep if it discovers that the buffer is empty. The consumer is awakened each time the producer inserts data into the buffer.
An insufficient response could lead to a standstill when both processes are awaiting activation.
Using the Producer-Consumer Class
- A LinkedList list is used to maintain a list of queued jobs.
- A variable capacity is used to determine whether or not the list is full.
- a technique to regulate the addition of items to and removal of items from this list so that neither is done when the list is full or empty.
Producer Consumer Problem Java Program
ProducerConsumerProblem.java
// importing the util package to use linked list
import java.util.LinkedList;
// Public class declaration
public class ProducerConsumerProblem{
// Main section of the program from where execution begins
public static void main(String[] args) throws InterruptedException
{
// A class object with the produce() and consume() functions.
final PC p = new PC();
// producer thread creation
// creation of the thread using thread class
Thread thread1 = new Thread(new Runnable() {
@Override
// rum method which is used to run the thread
public void run()
{
// try block is used to overcome the errors
try {
// calling the produce method
p.produce();
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
// consumer thread creation
Thread thread2 = new Thread(new Runnable() {
@Override
public void run()
{
try {
p.consume();
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
// starting both threads which are created
thread1.start();
thread2.start();
//
thread1.join();
thread2.join();
}
public static class PC {
// Create a list shared by producer and consumer
LinkedList<Integer> lt = new LinkedList<>();
int capacity = 2;
public void produce() throws InterruptedException
{
int v = 0;
while (true) {
synchronized (this)
{
// producer thread waits while buffer list is full
while (lt.size() == capacity)
wait();
System.out.println("Producer produced-" + v);
// Inserting the jobs into the buffer list
lt.add(v++);
// notifying the consumer thread to start consuming
notify();
// sleep method is used to stop the execution for certain amount of time
Thread.sleep(1000);
}
}
}
public void consume() throws InterruptedException
{
while (true) {
synchronized (this)
{
// consumer waits while buffer list is empty
while (lt.size() == 0)
wait();
int v = lt.removeFirst();
System.out.println("Consumer consumed-"+ v);
// Intimate the Producer thread
notify();
// consumner thread is forced to sleep until the producer thread produces
Thread.sleep(1000);
}
}
}
}
}
Output
Producer produced-0
Producer produced-1
Consumer consumed-0
Consumer consumed-1
Producer produced-2
Producer produced-3
Consumer consumed-2
Consumer consumed-3
Producer produced-4
Producer produced-5
Consumer consumed-4
Consumer consumed-5
Producer produced-6
Producer produced-7
Consumer consumed-6
Consumer consumed-7
Explanation
- A linked list of tasks and a list's capacity are added to the PC class (A class will both produce and consume methods) to ensure that the producer won't produce if the list is full.
- The initialization of the value in the Producer class is 0.
- In order to add values to the list, we also have an endless outer loop. We have a synchronized block inside of this loop that only allows a producer or consumer thread to execute at once.
- Before adding the jobs to the list, there is an inner loop that determines whether the job list is full. If it is, the producer thread relinquishes the intrinsic lock on the PC and enters the waiting state.
- If the list is empty, the control moves to the loop below, and a value is added to the list. We once more use an infinite loop in the Consumer class to pull a value from the list.
- We also have an inner loop within that determines whether the list is empty.
- If it is empty, we force the consumer thread to release the lock on the computer and transfer control to the producer thread so it can create more tasks.
- If the list has any items and is not empty, we repeat the loop and remove a piece of the list.
- We utilize the notify method at the conclusion of every statement in both approaches.
- If the list is empty, the control moves to the loop below, and a value is added to the list.
- We once more use an infinite loop in the Consumer class to pull a value from the list.
- We also have an inner loop within that determines whether the list is empty.
- If it is empty, we force the consumer thread to release the lock on the computer and transfer control to the producer thread so it can create more tasks.
- If the list has any items and is not empty, we repeat the loop and remove a piece of the list.
- We utilize the notify at the conclusion of every statement in both approaches.