/*
Author: Prasad
Date: Nov 29, 1999

*  This program demonstrates the multi-threading capability that Java
*  Language supports. It implements the classical producer-Consumer problem.
*  This program spawns 6 threads, 1 producer thread and 5 consumer threads.
*  The producer each time produces 5 resources, and waits for each of the
*  consumer thread to consume them. Once all the 5 resources are consumed
*  it again produces 5 resources. This process is repeated indefinitely.
*  Each of the five resources is identified by its ID which ranges from
*  1 to 5. Notice that when you run the applet for the program each time
*  each of the consumer thread gets a resource of different ID. Also
*  the resources are obtained by the consumer threads in a synchronized
*  manner, i.e. no two consumers get the same resource ID at the same time.
*
*/

import java.awt.*;   // For GUI, buttons etc.
import java.lang.*;
import java.lang.Object;
import java.util.Random;
import java.lang.Math;
import java.applet.*;
import java.awt.event.*;

public class producerconsumer extends Applet{

/* Create buttons, 1 producer button and 5 consumer buttons.
*/
	Button Producer1;	
	Button Consumer1;
	Button Consumer2;
	Button Consumer3;
	Button Consumer4;
	Button Consumer5;

	public void init(){
		/*
		 Add the buttons to the GUI.
		*/
		add(Producer1 = new Button("Producer"));
		add(Consumer1 = new Button("Consumer"));
		add(Consumer2 = new Button("Consumer"));
		add(Consumer3 = new Button("Consumer"));
		add(Consumer4 = new Button("Consumer"));
		add(Consumer5 = new Button("Consumer"));

		/* Create a common Resource queue object. This is the
                   object in which the producer will produce the resources
		   and consumer will consume the resources.
	 	*/
		ResourceQueue q = new ResourceQueue();

		/*
		 Create 1 producer thread, and 5 consumer threads. Pass the
		 same ResourceQueue object to each of them.
		*/
		new Producer(q);
		new Consumer(q, Consumer1);
		new Consumer(q, Consumer2);
		new Consumer(q, Consumer3);
		new Consumer(q, Consumer4);
		new Consumer(q, Consumer5);

		System.out.println("Press control");
		
	}

        
        
	/*
	 ResourceQueue is the object in which Producer will produce 
	 resources and consumers will consume resources.
	*/
	class ResourceQueue {
		// n is number of resources currently available.
		int n;  

		ResourceQueue(){n=0;}
	  
		/*
		 A synchronized method will allow only one thread to execute
		 the method at a time. All other threads that try to call this 
		 method on the same instance of the object will have to wait.
		*/
		/*
		This get() method could be potentially executed by a number of
		consumers. To avoid that we use the synchronized tag to
		qualify the method as a synchronous method.
		*/
		synchronized int get() {
			int temp;
			
			/*
			If n<=0, it implies that resources are not available
			for consumption, hence wait.
			*/
			while(this.n<=0) {
				try {
					wait();
				}
				catch(InterruptedException e){
				System.out.println("Interrupted exception");
				} }
			/*
			 Consume the resource. Decrement the number of
			 resources. If number of resources has become zero
			 notify the producer to produce.
			*/
			temp = this.n;
			this.n--;
			System.out.println("Got:" + temp);

			/*
			 If n<=0, then notify the producer to produce
			 resources.
			*/
			 if(this.n<=0)
			    notify(); 
			return temp;
		}



		/*
		 This method is used by the producer to put the resources back
		 in ResourceQueue object whenever number of resources goes
		 to zero.
		*/
		synchronized void put() {

	            /*
		     If number of resources is greater than zero, then wait.
	            */
		    while(this.n>0) {
				try {
					wait();
				}
				catch(InterruptedException e){
				   System.out.println("Interrupted exception");
				}
			}
			
		        Producer1.setLabel("Producing ...");
			System.out.println("producing ...");
				try {
					Thread.sleep(1000);
				     }catch(InterruptedException e){}
			this.n = 5;
			
			/*
			 Notify the consumers that resources are now 
			 available for consumption.
			*/
			notifyAll();
		    }
	}


        /*
	 The Consumer thread. Its only job is to consume resources when they
	 are available one at a time. Also after consuming a resource
	 it sleeps for a random amount of time before resuming.
	*/
	class Consumer implements Runnable {
		ResourceQueue q;
		Button B;
		
	    
	        /*
		  Constructor for the consumer thread.
		*/
	        Consumer(ResourceQueue q, Button B) {
			this.q = q;
			this.B = B;
			new Thread(this, "Consumer").start();
		}
	    
		public void run() {
			int temp;
			double value;
			Double sleeptime;
			Random r = new Random();

			/* Consume the resource using get() method.
			   Generate a random number between 0 and 1.
			   Multiply this with 200, and sleep for
			   so many milliseconds. This is done so that
			   each consumer thread sleeps for a different amount
			   of time and hence each time it gets a
			   different resource ID.
			   Repeat the process in a while loop.
			*/
			while(true) {
				temp = q.get();
				B.setLabel("Got :" +temp);
				try {
					value = r.nextDouble();
					sleeptime = new Double(value*200);
					Thread.sleep(sleeptime.intValue());
					}catch(InterruptedException e){}
			}
		}
	}


        /*
	 This is the producer thread.
	 In a loop, it keeps on producing resources by using the
	 put() method. Each time it produces it will add 5 resources
	 to the ResourceQueue object.
	*/
	class Producer implements Runnable {
		ResourceQueue q;
		Producer(ResourceQueue q) {
			this.q = q;
			new Thread(this, "Producer").start();
		}
		public void run() {
			int i=0;
			while(true) {
				q.put();
			}
		}
	}
	
											
	
		
			
		


}







