Thursday, July 4, 2013

Threads: Producer and Consumer Problem

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:
 1) Producer Consumer Pattern simple development. you can Code Producer and Consumer independently and Concurrently, they just need to know shared object.
 2) Producer doesn't need to know about who is consumer or how many consumers are there. Same is true with Consumer.
 3) Producer and Consumer can work with different speed. There is no risk of Consumer consuming half-baked item. In fact by monitoring consumer speed one can introduce more consumer for better utilization.

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