/*   Producers-Consumers with a 2-positions buffer */
/*   Multithreads implementation                   */
 
/*   File Producers_Consumers_2B.java              */
 
/*   ******************************************    */
/*                  Main program                   */
/*   ******************************************    */
 
public class Producers_Consumers_2B {
  public static void main(String[] args) {
    Buffer2 b = new Buffer2();
    new Producer(b,1).start();
    new Producer(b,2).start();
    new Producer(b,3).start();
    new Consumer(b,1).start();
    new Consumer(b,2).start();
  }
}

/*   ******************************************    */
/*                    Producer                     */
/*   ******************************************    */

class Producer extends Thread {
  private Buffer2 buff;
  private int id;
  public Producer(Buffer2 b, int n) { buff = b; id = n; }
  public void run() {
    try {
      while (!isInterrupted()) {
        sleep((long)(java.lang.Math.random()*1000)); 
        buff.put(id); System.out.println("Producer " + id + " puts a datum");
      }
    } catch (InterruptedException e) { return; }
  }
}

/*   ******************************************    */
/*                    Consumer                     */
/*   ******************************************    */

class Consumer extends Thread {
  private Buffer2 buff;
  private int id;
  public Consumer(Buffer2 b, int n) { buff = b; id = n; }
  public void run() {
    try {
      while (!isInterrupted()) {
        sleep((long)(java.lang.Math.random()*1000)); 
        int k = buff.get();
        System.out.println("consumer " + id + " has retrieved the datum put by producer " + k);
      }
    } catch (InterruptedException e) { return; }
  }
}

/*   ******************************************    */
/*                      Buffer                     */
/*   ******************************************    */

class Buffer1 {
  private int content;
  private boolean is_empty;
  public Buffer1() { is_empty = true; }
  public synchronized void put(int k) 
    throws InterruptedException
  {    
    while (!is_empty) wait();
    content = k;
    is_empty = false;
    notifyAll(); 
  }
  public synchronized int get() 
    throws InterruptedException
  {
    while (is_empty) wait();
    is_empty = true;
    notifyAll(); 
    return content;
  }
  public synchronized boolean is_full() { return !is_empty; }
}

class Buffer2 {
  private Buffer1 first, second;
  private class Demon extends Thread {
    private Buffer1 from, to; //Buffer1 is the 1 pos. buffer, defined as before
    public Demon(Buffer1 f, Buffer1 t){ from = f; to = t; }
    public void run() {
      try { 
        while (!isInterrupted()) { 
	  if (!(to.is_full())) 
            synchronized (from) { int k = from.get();  to.put(k); }
        } //synch. on from is to avoid the presence of 3 data at the same time
      } catch (InterruptedException e) { return; }
    }
  }
  public Buffer2(){
    first = new Buffer1();
    second = new Buffer1();
    new Demon(first,second).start(); 
  }
  public void put(int k) 
    throws InterruptedException {    
    first.put(k);
  }
  public int get() 
    throws InterruptedException {
    return second.get();
  }
}

/*   ******************************************    */
  
