0% found this document useful (0 votes)
39 views6 pages

Java Producer-Consumer Implementation

The document describes implementing a producer-consumer problem in Java using threads. The producer's job is to generate data and put it in a fixed-size buffer, while the consumer removes data from the buffer. To prevent overflow or empty buffer issues, the producer sleeps if the buffer is full and the consumer sleeps if it is empty. When data is added or removed, the sleeping thread is notified to prevent deadlock. The code implements classes for the producer, consumer and shared buffer (Q class), and creates producer and consumer threads to run the program.

Uploaded by

Mamta Bansal
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
39 views6 pages

Java Producer-Consumer Implementation

The document describes implementing a producer-consumer problem in Java using threads. The producer's job is to generate data and put it in a fixed-size buffer, while the consumer removes data from the buffer. To prevent overflow or empty buffer issues, the producer sleeps if the buffer is full and the consumer sleeps if it is empty. When data is added or removed, the sleeping thread is notified to prevent deadlock. The code implements classes for the producer, consumer and shared buffer (Q class), and creates producer and consumer threads to run the program.

Uploaded by

Mamta Bansal
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

EXPERIMENT NO.

– 13
AIM – Write a Program to Implement Producer Consumer problem in Java
using Thread.

Producer-Consumer solution using threads in Java

In computing, the producer-consumer problem (also known as the bounded-


buffer problem) is a classic example of a multi-process synchronization
problem. The problem describes two processes, the producer and the consumer,
which share a common, fixed-size buffer used as a queue.

· The producer’s job is to generate data, put it into the buffer, and start again.

· At the same time, the consumer is consuming the data (i.e. removing it from
the buffer), one piece at a time.

Problem:

To make sure that the producer won’t try to add data into the buffer if it’s full
and that the consumer won’t try to remove data from an empty buffer.

Solution:

The producer is to either go to sleep or discard data if the buffer is full. The next
time the consumer removes an item from the buffer, it notifies the producer,
who starts to fill the buffer again. In the same way, the consumer can go to sleep
if it finds the buffer to be empty. The next time the producer puts data into the
buffer, it wakes up the sleeping consumer.

An inadequate solution could result in a deadlock where both processes are


wait- ing to be awakened.
CODE –

import [Link];

class Q {

int n;

boolean signal = false;

synchronized int consume() {

while(!signal)

try {

[Link]("Consumer thread sleeps");

wait();

} catch(InterruptedException e)

{ [Link]("InterruptedException

caught");

[Link]("Consumer thread awakes");

[Link]("Consumed: " + n);

signal = false;

notify();

return n;

synchronized void produce(int n) {

while(signal)

try {

[Link]("Producer thread sleeps");


wait();

} catch(InterruptedException e)

{ [Link]("InterruptedException

caught");

this.n = n;

[Link]("Producer thread awakes");

signal = true;

[Link]("Produced: " + n);

notify();

class Producer implements Runnable {

Q q;

int n;

Producer(Q q,int n) {

this.n=n;

this.q = q;

new Thread(this, "Producer").start();

public void run()

{ [Link]("Producer thread

created"); int i = 0;

while(i<n)

{ [Link](+

+i);
}

[Link]("Producer thread sleeps");

class Consumer implements Runnable {

Q q;

int n;

Consumer(Q q,int n) {

this.q = q;

this.n=n;

new Thread(this, "Consumer").start();

public void run()

{ [Link]("Consumer thread

created"); while(true) {

[Link]();

public class synch {

public static void main(String[] args) {

int n;

[Link]("Enter the production limit");

Scanner sc=new Scanner([Link]);


n=[Link]();

[Link]("The production limit is "+n);

Q q = new Q();

new Producer(q,n);

new Consumer(q,n);

}
OUTPUT –

Common questions

Powered by AI

The 'notify()' method plays a crucial role in the Java solution for the producer-consumer problem by waking up one waiting thread that is waiting on the object's monitor. This method helps transition the consumer from waiting to active when the producer adds an item to a previously empty buffer, or vice versa, allowing the producer to become active when a slot opens in a previously full buffer. It ensures that changes in resource availability are communicated effectively between threads, maintaining the workflow without unnecessary delays or resource blocking .

Deadlocks can occur in the producer-consumer problem if both processes are waiting for each other to proceed because neither can signal the other to change state. For example, if both the producer and consumer go to sleep because the buffer is full or empty, respectively, and no other process can awaken them, a deadlock situation arises. Strategies to prevent deadlocks include using locks carefully and ensuring a fall-back condition, such as a timeout on wait operations, which can break cycles of dependency and allow processes to recover independently .

The producer and consumer threads are initialized by instantiating their respective classes, 'Producer' and 'Consumer', which implement the Runnable interface. Each class takes an instance of the shared resource object 'Q' and a production limit or condition. A new thread is created and started via 'new Thread(this, "Producer").start()' within the constructor of each class, thereby invoking the 'run()' method, which embodies the logic for producing or consuming. This setup allows threads to execute concurrently, managing their operations via the methods defined in the shared resource instance .

Waiting is important in the producer-consumer problem to prevent the producer from adding to a full buffer and the consumer from removing from an empty buffer, thus avoiding buffer overflows and element underflows. In Java, this is achieved using the 'wait()' method, which causes a thread to wait until another thread invokes 'notify()' on the same object. When a producer thread finds the buffer full, it calls 'wait()'. Similarly, the consumer thread waits if the buffer is empty, thus ensuring that operations only proceed when appropriate conditions are met .

The Java implementation manages concurrent access to the shared buffer using synchronized methods and a signaling mechanism with boolean flags. The 'synchronized' keyword ensures that a block of code can only be executed by one thread at a time, preventing race conditions. The 'wait()' and 'notify()' methods are used to manage the sleeping and waking of threads based on buffer status. When the buffer is full, the producer thread waits, and when the buffer is empty, the consumer thread waits. This synchronization avoids concurrent write/read actions that could corrupt the shared resource state .

The boolean variable, referred to as 'signal' in the Java program, acts as a flag to indicate whether the buffer is currently ready for a producer or consumer operation. When 'signal' is false, the consumer waits, allowing the producer to add items to the buffer, and when full, it sets 'signal' to true. This controls the flow of execution between alternating the producer's and consumer's operations to ensure synchroneity in data handling, preventing simultaneous modifications or data consumption from an improperly filled buffer .

The 'synchronized' keyword in the Java program contributes to thread-safe operations by ensuring that the methods 'consume()' and 'produce()' block access to ensure only one thread can enter either method at a time. This prevents simultaneous modifications or access to shared resources, thereby avoiding race conditions and allowing orderly access control to the shared buffer. It acts as a lock device, keeping the buffer's modification and access states atomic and consistent across thread invocations .

The Java program implements protective measures by using a signaling boolean and synchronized control. The boolean 'signal' indicates the buffer's state, i.e., whether it's ready for a producer or a consumer action. When the signal is set to true, the consumer proceeds and vice versa. Additionally, the 'wait()' method is used when the respective action cannot be performed, such as when the buffer is full for producers or empty for consumers, preventing inappropriate buffer manipulation and maintaining its integrity .

The producer-consumer problem addresses the synchronization challenges of coordinating access to a shared, fixed-size buffer between two processes. The primary challenges include ensuring that the producer does not add data to a full buffer and that the consumer does not remove data from an empty buffer, which could lead to data loss or access violations. The solution outlined involves using sleep and wake mechanisms to manage the state of the buffer and prevent deadlocks, ensuring these two processes can operate concurrently without conflict .

If 'notify()' is replaced with 'notifyAll()' in a heavily loaded system, every waiting thread would be awakened when the buffer state changes, rather than a single thread. This could potentially cause a burst of resource requests, leading to increased CPU usage and possible thread contention when multiple threads contest for the same lock. While it might aid in higher responsiveness in certain situations, in this bounded-buffer scenario, it might decrease performance efficiency and could lead to threads going back to waiting, causing unnecessary overhead .

You might also like