Concurrency pattern: Producer-consumer
Written by Mottola Michele - Italy - Reggio Emilia   
Monday, 07 September 2015 09:39
Last Updated on Tuesday, 08 September 2015 18:39
AddThis Social Bookmark Button

What is the problem

Basically this pattern solve the problem of a buffer, in which one or more Producer can push a data and one or more Consumer can pull a data. There are a couple of problems that can arise: the first is catch to ’race condition’, indeed since to the buffer can access more productors and consumers at the same time, can arise problems of inconsistency data. The second problem is catch to ’deadlock’, mean a lock of system.

How this pattern work

In this pattern, the problem of ’race condition’ arise, for example, if we allow to do, at the same time, push and pull operation. In this case, since both methos changes the internal state of the buffer object, they can read not right data. Generally to avoid ’race condition’ problems on an obbject it’s enough use the ’single threaded execution’ pattern.

Single threaded execution

Basically, in our case, the ’single threaded execution’ allows to the methos of object of type Buffer, signed as ’guarded’, may be executed by only one thread at same time. The thread that may use a method signed ’guarded’ obtain the lock of buffer’s object and it release the lock only after has finished the call.

Deadlock

There is still the problem of deadlock. If we modelling the behaviour of this system with only two tread, one for the producer and one for the consumer, using only the ’single threaded execution’ we have an activity diagram like this:

From this we can understand how work the consumer thread. It first checks the availability of lock, if it is free it obtain, instead if it is busy it go in queue and wait until is free. Then it checks if there are data in buffer, if there are it pull it, finish its execution and release the lock. But what happen if there aren’t data on buffer? It wait until the buffer get push from the producer. But the thread of producer may not obtain the lock because it is keep from the thread of consumer. So there is a deadlock.
To solve the question we need of another pattern: the ’guarded suspension’. Basically this pattern use a notification system between the thread. In our case, practically, if the consumer doesn’t see data in the buffer, not only it get in wait but also release the lock. In this way the producer can obtain it and push a data in the buffer. When the producer has finisched it send a notify to the suspended thread that obtain the lock and continue its execution.

Class diagram

Java implementation

The java implementation is very quick. Basically we need to implement push() and pull() methods in Buffer. It’s to notice that in java, a method signed as ’guarded’ is implemented using the keyword ’synchronized’. Besides the notification system between the thread, in java is implemented by wait() and notify() methods.

Buffer.java

package producerConsumer;

public class Buffer {

	private int size;

	synchronized public void push(){

		this.size++;
		System.out.println("i have added data to buffer");
		notify();
		
		
		
	}
	
	synchronized public void pull(){

		while(this.size==0)
			try {
				System.out.println("i am in wait");				
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		this.size--;
		System.out.println("i have fetched data from buffer");
		
	}

	public int getSize() {
		return size;
	}

	public Buffer(int size) {
		super();
		this.size = size;
	}
	
	
}

Producer.java

package producerConsumer;

public class Producer extends Thread {

	
	private Buffer b;
	
	@Override
	public void run() {

		b.push();
	}

	public Producer(Buffer b) {
		super();
		this.b = b;
	}
	
	

}


Consumer.java

package producerConsumer;

public class Consumer extends Thread {

	
	private Buffer b;
	
	@Override
	public void run() {

		b.pull();
		
	}

	public Consumer(Buffer b) {
		super();
		this.b = b;
	}
	

}


To simulate the behaviour of this system we can use java thread. So we need one thread to simulate the behavour of Producer and one for the Consumer. The test i show here is to show how the thread of consumer work when the buffer is empty. The consumer thread will wait until the producer doesn’t push a data in buffer. Only then the consumer will continue it’s execution fetching a data from buffer.

Test.java


package producerConsumer;

import java.io.IOException;

public class Test {

	public static void main(String[] args) {
		
		
		Buffer b = new Buffer(0);
		
		Consumer c = new Consumer(b);
		c.start();
		
		
		

		System.out.println("press key to continue");
		try {
			System.in.read();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Producer p = new Producer(b);
		p.start();
		

		

	}

}

Output

When i run the program, the thread of consumer start, instead the thread of producer will wait until i press a button. This will be the message

press key to continue
i am in wait

it show that the consumer is get in wait because the buffer was empty
Only when i pressed a key the thread of producer starts, add a data to the buffer and notify the waiting thread of consumer that can pull a data.

i have added data to buffer
i have fetched data from buffer

 

You have no rights to post comments
You have to register to add comments