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

import java.util.*;

/** Producer-Consumer demo.
 * @author Jaanus Poial
 * @version 0.3 spring 2006
 * @since 1.5
 */
public class RunnableCP5 {

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

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

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

} // RunnableCP5


/** Producer. */
class Producer implements Runnable {

   /** producing thread */
   Thread pthread;

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

   /** producer label */
   String name;

   /** producer thread constructor */
   Producer (int i, String s) {
      pthread = new Thread (this); // "this" is Runnable
      tick = i;
      name = s;
      pthread.start();
   }

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

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

} // Producer


/** Consumer. */
class Consumer implements Runnable {

   /** consumer thread */
   Thread cthread;
   
   /** time to consume an item in milliseconds */
   int tick;

   /** consumer label */
   String name;

   /** consumer thread constructor */
   Consumer (int i, String s) {
      cthread = new Thread (this); // "this" is Runnable
      tick = i;
      name = s;
      cthread.start();
   }

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

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

} // Consumer 

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

