import java.lang.Thread;
import java.lang.Runnable;

/**
* @function :
* M个生产者,N个消费者=多个线程
* 需要一个容器来放置生产的物品=共享区域
* synchronized同步非静态方法时,是对该类的实例(对象)加锁.
* 同一时刻该对象内只能有一个synchronized方法执行
* 一般我们都使用notifyAll,唤醒所有阻塞在该对象上的线程
* notify只是随机唤醒一个线程
* @author     :Eric He
* @company    :fdu
* @date         :2009-12-9
*/

public class TestSyn {
  public static void main(String[] args){
    //生成一个产品的容器
    Container c = new Container(5);
    //生成3个生产者,分别准备生产4,8,6个产品
    new Thread( new Producer(c,1, 4)).start();
    new Thread( new Producer(c,2, 5)).start();
    new Thread( new Producer(c,3, 3)).start();
    //生成2个消费者,每次均消费一个产品
    new Thread( new Consumer(c,1)).start();
    new Thread( new Consumer(c,2)).start();
    
  }
    
}

class Container{
  private int currentSize;
  private Product[] pros;
  Container(int counts){
    assert(counts>0);
    this.pros = new Product[counts];
    currentSize = 0;
  }
  public synchronized void put(Product p){
    //使用while,不能用if,因为在被唤醒后仍需要检查容器是否已满
    while(currentSize == pros.length ){
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    this.notifyAll();
    pros[currentSize++]=p;
  }
  public synchronized Product get(){
    while(currentSize == 0){
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    this.notifyAll();
    return pros[--currentSize];
    
  }  
}

class Producer implements Runnable{
  private Container c;
  //预生产产品的数量
  private int preCounts;
  //生产者id
  private int producerID;
  Producer(Container c ,int producerID, int preCounts){
    this.c = c;
    this.preCounts = preCounts;
    this.producerID = producerID;
  }
  public void run(){
    for(int i= 0; i<preCounts; i++){
      Product p =new Product(i);
      c.put(p);
      System.out.println("生成者"+producerID+"生产 "+p);
      try {
        Thread.sleep((long)Math.random()*1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
  
}

class Consumer implements Runnable{
  private Container c;
  private int consumerID;
  Consumer(Container c, int consumerID){
    this.c = c;
    this.consumerID = consumerID;
  }
  public void run(){
    while(true){
      Product p = c.get();
      System.out.println("消费者"+consumerID+"消费 "+p);
    }
  }
}

class Product{
  private int proID;
  Product(int i){
    proID = i;
  }
  public String toString(){
    return "[Product"+proID+"]";
  }
}