// <pre>
// ----------------------------------------------------------------------------
// file: ConsProd.java

import java.util.*;

/** Producer-Consumer demo.
 * @author Jaanus Poial
 * @version 0.2
 * @since 1.2
 */
public class ConsProd {

   /** buffer capacity */
   public static final int BUFLEN = 6;

   /** shared buffer */
   public static ArrayList buf = new ArrayList (BUFLEN);

   /** Main method */
   public static void main (String[] param) {
      int producer1_tick  = 2000; //millisec
      int producer2_tick  = 7000; //millisec
      int consumer1_tick  = 3000; //millisec
      int consumer2_tick  = 1000; //millisec
      Producer  producer1 = new Producer (producer1_tick,  "Pro1");
      Producer  producer2 = new Producer (producer2_tick,  "Pro2");
      Consumer consumer1  = new Consumer (consumer1_tick, "Con1");
      Consumer consumer2  = new Consumer (consumer2_tick, "Con2");
      System.out.println ("Buffer capacity: " + BUFLEN);
      producer1.start();
      producer2.start();
      consumer1.start();
      consumer2.start();
   } // main

} // ConsProd


/** Producer thread. */
class Producer extends Thread {

   /** time to produce an item in milliseconds */
   int tick;

   /** producer label */
   String name;

   /** producer thread constructor */
   Producer (int i, String s) {
      tick = i;
      name = s;
   }

   /** producer thread content */
   public void run() {
      try {
         while (true) { // infinite loop
            produce();  // produce an item after each tick
            sleep (tick);
         }
      }
      catch (InterruptedException e) {
      }
   } // run

   /** producing process */
   private void produce()
      throws InterruptedException {
         synchronized (ConsProd.buf) {
            while (ConsProd.buf.size() >= ConsProd.BUFLEN) {
               ConsProd.buf.wait(); // buffer is full, wait
            }
            ConsProd.buf.add ("(" + name + " > " +
               new Date().toString() + ")"); // timestamp
            System.out.println (name + " > " + 
               ConsProd.buf.get (ConsProd.buf.size()-1) +
               " " + tick + " " + ConsProd.buf.size());
            ConsProd.buf.notifyAll();
         } // end of critical section
   } // produce

} // Producer


/** Consumer thread. */
class Consumer extends Thread {
   
   /** time to consume an item in milliseconds */
   int tick;

   /** consumer label */
   String name;

   /** consumer thread constructor */
   Consumer (int i, String s) {
      tick = i;
      name = s;
   }

   /** consumer thread content */
   public void run() {
      try {
         while (true) { // infinite loop
            consume();  // consume an item after each tick
            sleep (tick);
         }
      }
      catch (InterruptedException e) {
      }
   } // run

   /** consume an item */
   private void consume()
      throws InterruptedException {
         synchronized (ConsProd.buf) {
            while (ConsProd.buf.size() <= 0) {
               ConsProd.buf.wait(); // buffer is empty, wait
            }
            String message = (String)ConsProd.buf.get (0);
            System.out.println (name + " < " +
               message + " " + tick);
            ConsProd.buf.remove (0);
            ConsProd.buf.notifyAll();
         } // end of critical section
   } // consume

} // Consumer 

// end of file
// ----------------------------------------------------------------------------
// </pre>

