I was thinking to improve my threading fundamentals, so I
planned to make few notes for myself that will assist me in future (Obviously
at the time of Interviews). This post is a sticky notes post for me.
Problem Statement
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, who share a
common, fixed-size buffer used as a queue. The producer's job is to generate a
piece of 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. The problem is 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
Producer and consumer should produce and consume some
resource. For the simplicity purpose I am taking minimalist object in the
Information object.
package com.thread.problem;
public class Information {
private
String message;
private
boolean isAvailToWrite = false;
public
boolean isAvailable(){
return
isAvailToWrite;
}
public
boolean isUnavailable(){
return
!isAvailToWrite;
}
public
void put(String msg) throws InterruptedException{
synchronized
(this) {
while(isUnavailable()){
System.out.println("Producer will wait
now.");
wait();
}
message
= msg;
isAvailToWrite
= true;
notify();
}
}
public
String get() throws InterruptedException{
synchronized
(this) {
while(isAvailable()){
System.out.println("Consumer will wait
now.");
wait();
}
isAvailToWrite
= false;
notify();
return
message;
}
}
}
Information class has 4 methods.
isUnavailable and isAvailable are providing the information
whether the Information class can accept more data or provide any data.
put and get are synchronized method, which are setting or
retrieving data from the Information object.
In case of get request, if data is not available, then
current running thread will go to wait state, and wait until put operation is
not performed on the Information object. It holds true for put operation as
well.
Now information object must be produced and consumed with
different threads. So we will need producer thread to produce information
object, and we will need consumer thread to consume the produced objects.
package com.thread.problem;
public class Consumer implements
Runnable {
private Information pool;
public
Consumer(Information pool) {
this.pool
= pool;
}
@Override
public void run()
{
for(int
i = 0; i < 20; i++){
try
{
System.out.println("Retrieved
String:::: " + pool.get());
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.thread.problem;
public class Producer implements
Runnable {
private Information pool;
public
Producer(Information pool) {
this.pool
= pool;
}
@Override
public void run()
{
for(int
i = 0; i < 20; i++){
try
{
pool.put("String
" + i );
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Constructors is created so that shared objects can be passed
to Consumer and producer thread.
Once everything is set up, now we need to demonstrate
whether our solution works. So let write main method within another class which
will start both the threads.
package com.thread.problem;
public class Demonstration {
public static void
main(String[] args) {
Information pool
= new Information();
Thread c = new
Thread(new Consumer(pool));
Thread p = new
Thread(new Producer(pool));
p.start();
c.start();
}
}
Output for the following program will be as following.
Producer will wait now.
Producer will wait now.
Retrieved String:::: String 0
Producer will wait now.
Retrieved String:::: String 1
Producer will wait now.
Retrieved String:::: String 2
Producer will wait now.
Retrieved String:::: String 3
Retrieved String:::: String 4
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 5
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 6
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 7
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 8
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 9
Producer will wait now.
Retrieved String:::: String 10
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 11
Producer will wait now.
Retrieved String:::: String 12
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 13
Producer will wait now.
Retrieved String:::: String 14
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 15
Consumer will wait now.
Producer will wait now.
Producer will wait now.
Retrieved String:::: String 16
Retrieved String:::: String 17
Consumer will wait now.
Producer will wait now.
Retrieved String:::: String 18
Consumer will wait now.
Retrieved String:::: String 19
This output may differ from compiler to compiler.
Its indeed a useful design pattern and
used most commonly while writing multi-threaded or concurrent code. here is few of its benefit:
4) Separating producer and Consumer functionality result in
more clean, readable and manageable code.
There are other multiple ways to solve this problem. This example is used to for naive users. Users who have started leaning java language very recently. It is helpful for them.
No comments:
Post a Comment