import java.util.*; // for Random // This class contains Threads which are either Producers or Consumers. A Producer puts something into a shared // array (buffer) and Consumers remove something from the shared array. With Threads controlling the Producers // and Consumers, we cannot guarantee the order that the Ps and Cs will producer/consume. Problem: Consumer cannot // consume from an empty buffer and Producers cannot produce into a full buffer. We will fix these problems in // later implementations public class ProducerConsumer { private static int[] buffer; // the shared buffer which is a queue, so we insert at rear, remove at front private static int front, rear; // indices of the current front and rear private static Random g; // randomly decide how much each Thread waits before Producing/Consuming private static Thread t1, t2, t3, t4, t5; // we have 1 Thread for each of the Producers (2) and Consumers (3) private static boolean done; // indicates when the Producers and Consumers should stop public static void main(String[] args) { buffer=new int[10]; // we will use a small buffer to demonstrate the Producer overflowing the buffer front=0; // initialize our front and rear pointers rear=0; g=new Random(); done=false; // always set to false in this run Producer p1=new Producer("Producer 1"); // create our producers and consumers, giving them names Producer p2=new Producer("Producer 2"); // so that we can see what is going on during the run Consumer c1=new Consumer("Consumer 1"); Consumer c2=new Consumer("Consumer 2"); Consumer c3=new Consumer("Consumer 3"); t1=new Thread(p1); // create the five Threads t2=new Thread(p2); t3=new Thread(c1); t4=new Thread(c2); t5=new Thread(c3); t1.start(); // start the five Threads t2.start(); t3.start(); t4.start(); t5.start(); } // A Producer will be a Thread which, when started, will have an infinite while loop which causes the // Thread to alternate between sleeping and producing an int value to be inserted into the buffer public static class Producer implements Runnable { private String name; // so we can see which Thread is doing what public Producer(String name){ this.name=name; } public void run() // when run, this Thread loops forever (we will fix this later) { // sleeping a random amount and then Producing a random int to be int temp; // placed at the current rear of the buffer while(!done) { try{ temp=g.nextInt(1000); // amount to sleep in milliseconds, 1000 = 1 second System.out.println(name + " sleeping for " + temp); // tell user whats going on Thread.sleep(temp); // and then sleep } catch(InterruptedException e) { // sleep can throw this Exception so we have to catch it System.out.println(e); } temp=g.nextInt(10)+1; // get a random int (1-10) to insert into the buffer, meaningless data System.out.println("Producer " + name + " producing " + temp + " at " + rear); buffer[rear]=temp; // insert new int into buffer at the current rear rear++; // and reset rear to be the next array location } } } // A Consumer is a Thread that, when started, will have an infinite loop which will cause the Thread to // alternate between sleeping and consuming from the buffer. What happens if it attempts to consume // an element from the buffer that has not yet been placed there? public static class Consumer implements Runnable { private String name; // Consumer name so we can see whats going on public Consumer(String name){ this.name=name; } public void run() { int temp; while(!done) // loop forever, to be fixed later { try{ temp=g.nextInt(1000); // try to sleep a random amount of time System.out.println(name + " sleeping for " + temp); Thread.sleep(temp); } catch(InterruptedException e) // catch the exception that sleep can throw { System.out.println(e); } temp=buffer[front]; // get the next datum (if front>=rear, we have a problem!) System.out.println("Consumer " + name + " consuming " + temp + " at " + front); front++; // point at next buffer element for next Consumer } } } } // when run, we will see the Producers and Consumers operate in a random order (based on the OS scheduling) // and each Thread sleeps a different amount of time, so it is entirely unpredictable which Thread is // going to operate next. If a Consumer attempts to consume from the buffer, it may pull out a value // that hasn't been placed into the buffer yet. This will be the case if the value is 0 (the int array // defaults to all 0s). If the value is 1-10, we know it was a produced value. Additionally, the Producer // may try to produce once the buffer is full (rear>9) resulting in an ArrayIndexOutOfBoundsException. // We will fix the Consumer problem in the next program and the Producer problem in the 3rd program.