生产者消费者问题

描述


有一群生产者在生产产品,并将这些产品提供给消费者去消费。为使生产者与消费者能够并发执行,在两者之间设置一个具有 n 个缓冲区的缓冲池,生产者将他所生产的产品放入一个缓冲区中;消费者可从一个缓冲区中取走产品去消费。尽管所有的生产者和消费者都是以异步方式运行,但他们之间必须保持同步,即不允许消费者到一个空缓冲区去取产品;也不允许生产者向一个已装满产品且尚未被取走的缓冲区投放产品。


假设缓冲区为100。



思路


1. 生产者和消费者都有两个状态,生产/消费和等待。


2. 共享资源就是库存,生产者生产产品放入缓冲区(库存增加),消费者消费缓冲区中的产品(库存减少)。


3. 生产者生产产品影响消费者消费(有产品可以消费了),消费者消费产品影响生产者(库存空了可以生产了)。



步骤


1. 设计3个类,一个资源类,一个生产者类,一个消费者类。



资源类


  |--(常量)产品仓库大小(缓冲区)


  |--(变量)产品仓库


  |--(变量)生产的产品编号


  |--(变量)消费的产品编号


  |--(变量)库存


  |--(变量)锁


  |--(变量)锁上2个监视器,分别对于生产者和消费者


  |--(变量)运行标志


  |--(方法)仓库是否已满


  |--(方法)仓库是否已空


  |--(方法)是否正在运行(返回运行标志)


  |--(方法)终止程序运行


  |--(方法)生产


  |--(方法)消费



生产者类


  |--(变量)资源对象


  |--(方法)循环生产



消费者类


  |--(变量)资源对象


  |--(方法)循环消费



2. 生产前判断仓库是否已满,如果已满则等待,不满则生产,并唤醒消费者。


3. 消费前判断仓库是否已空,如果已空则等待,不空则消费,并唤醒生产者。



程序


import java.util.concurrent.locks.ReentrantLock;
    
    import java.util.concurrent.locks.Lock;
    
    import java.util.concurrent.locks.Condition;
    
     
    
    class Resource {
    
      public static final int MAX = 100;
    
      private int front = 0; // 生产的产品编号,生产一个就加1,并打印。
    
      private int rear = 0; // 消费的产品编号,消费一个就加1,并打印。
    
      private int count = 0; // 仓库中产品的数量。
    
     
    
      private final Object[] items = new Object[ MAX];
    
      private boolean runflag = true;
    
     
    
      private final Lock lock = new ReentrantLock();
    
      private final Condition notFull = lock.newCondition();
    
      private final Condition notEmpty = lock.newCondition();
    
     
    
      public boolean isFull() // 判断仓库是否满
    
      {
    
        return ( count == items. length);
    
      }
    
     
    
      public boolean isEmpty() // 判断仓库是否空
    
      {
    
        return ( count == 0);
    
      }
    
     
    
      public boolean isRunnable() // 生产和消费是否可执行
    
      {
    
        return runflag;
    
      }
    
     
    
      public void stopRun() // 终止生产和消费
    
      {
    
        runflag = false;
    
      }
    
     
    
      public void put(Object x) // 生产函数
    
      {
    
        lock.lock();
    
        try {
    
          while (isFull()) { // 如果仓库已满,则生产(仓库非满)等待。
    
            System.out.println(Thread.currentThread().getName() + " wait!");
    
            try {
    
              notFull.await();
    
            } catch (InterruptedException e) {} // 暂时不做异常处理
    
          }
    
          items[front] = x;
    
          System.out.println(Thread. currentThread().getName() + "...生产 = " + front + "...库存 = "
    
              + ++ count); // 显示线程,生产/消费,指针以及库存。
    
          front = (front + 1) % MAX; // 当指针到数组边界后从头开始循环。
    
          notEmpty.signal(); // 已经生产出产品,唤醒消费者(仓库非空)。
    
        } finally {
    
          lock.unlock(); // 锁的释放是必要行为,所以放在 finally 语句中。
    
        }
    
      }
    
     
    
      public Object take() // 消费函数
    
      {
    
        lock.lock();
    
        try {
    
          // 这里只能使用while而不能使用 if-else 组合,因为 return 语句必须在 try 中,而不可以在 else 中。
    
          while (isEmpty()) { // 如果仓库已空,则消费(仓库非空)等待。
    
            System.out.println(Thread.currentThread().getName() + " wait!");
    
            try {
    
              notEmpty.await();
    
            } catch (InterruptedException e) {} // 暂时不做异常处理
    
          }
    
          Object x = items[rear]; // 将指针指向的元素拿出。
    
          System.out.println(Thread. currentThread().getName() + "...消费 = " + rear + "...库存 = "
    
              + -- count); // 显示线程,生产/消费,指针以及库存。
    
          rear = (rear + 1) % MAX;
    
          notFull.signal();
    
          return x; // 将拿出的元素给消费者消费。
    
        } finally {
    
          lock.unlock();
    
        }
    
      }
    
    }
    
     
    
     
    
    class Producer implements Runnable {
    
      private Resource r;
    
     
    
      public Producer(Resource r) // 将公共的资源(仓库)传入构造函数
    
      {
    
        this.r = r;
    
      }
    
     
    
      public void run() {
    
        while ( r.isRunnable()) // 当生产消费可执行时开始生产
    
        {
    
          r.put(new Object());
    
        }
    
      }
    
    }
    
     
    
    class Consumer implements Runnable {
    
      private Resource r;
    
     
    
      public Consumer(Resource r) {
    
        this.r = r;
    
      }
    
     
    
      public void run() {
    
        while ( r.isRunnable()) {
    
          r.take();
    
        }
    
      }
    
    }
    
     
    
    public class ThreadProducerConsumer {
    
      public static void main(String[] args) {
    
        Resource r = new Resource();
    
        Producer p = new Producer( r);
    
        Consumer c = new Consumer( r);
    
        new Thread( p).start();
    
        new Thread( p).start();
    
        new Thread( c).start();
    
        new Thread( c).start();
    
        // 生产消费执行3秒后停止
    
        try {
    
          Thread.sleep(3000);
    
        } catch (InterruptedException e) { /* TODO:处理代码 */}
    
        r.stopRun();
    
      }
    
    }


运行结果(部分)


Thread-1...生产 = 88...库存 = 96


Thread-1...生产 = 89...库存 = 97


Thread-1...生产 = 90...库存 = 98


Thread-1...生产 = 91...库存 = 99


Thread-1...生产 = 92...库存 = 100


Thread-1 wait!


Thread-0 wait!


Thread-3...消费 = 93...库存 = 99


Thread-3...消费 = 94...库存 = 98


Thread-3...消费 = 95...库存 = 97


Thread-3...消费 = 96...库存 = 96



哲学家进餐问题

描述


五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。



思路


1. 哲学家共有两个状态:思考和进餐。只有进餐用到临界资源:筷子。


2. 哲学家使用临界资源有两个方法:使用筷子,放弃筷子,分别对应哲学家两个状态。


3. 哲学家使用筷子始终影响的是邻座的两个哲学家。



步骤


1. 设计两个类,一个筷子类,一个哲学家类。



筷子类


  |--(变量)5只筷子


  |--(变量)锁


  |--(变量)锁上5个监视器方法,分别对应5个哲学家


  |--(方法)思考


  |--(方法)进餐



哲学家类


  |--(变量)筷子类资源


  |--(变量)哲学家编号


  |--(变量)哲学家工作标志


  |--(方法)哲学家工作:包含思考和进餐


  |--(方法)哲学家停止工作


  |--(方法)run方法:条件循环执行工作方法。



2. 当哲学家进餐,则判断左右两只筷子是否被占用,如果没有,则标记这两只筷子使用,哲学家进餐,进餐人数加1;如果筷子被占用,则哲学家等待。


3. 当哲学家思考,则将左右两只筷子标记未使用,进餐人数减1,唤醒左边和右边两个哲学家。


4. 当哲学家停止工作,那么工作标记将被复位,哲学家工作循环停止。



程序


import java.util.concurrent.locks.ReentrantLock;
    
    import java.util.concurrent.locks.Lock;
    
    import java.util.concurrent.locks.Condition;
    
     
    
    class Chopsticks {
    
      // 5只筷子使用标记
    
      private boolean[] chops = {false, false, false, false, false};
    
      private int eatcount = 0; // 进餐人数统计
    
      private final Lock lock = new ReentrantLock();
    
      private final Condition[] ph_monitor = new Condition[5];
    
      {
    
        for (int i = 0; i < 5; i++) {
    
          // 初始化代码块,初始化针对5个哲学家监视器类
    
          ph_monitor[i] = lock.newCondition();
    
        }
    
      }
    
     
    
      public void eat( int ph) {
    
        lock.lock();
    
        try {
    
          // 如果左右两只筷子在用,那么该哲学家等待
    
          while ( chops[ ph] || chops[( ph + 1) % 5]) {
    
            System. out.println("PH." + ph + " Waiting..." );
    
            try {
    
              ph_monitor[ ph].await();
    
            } catch (InterruptedException e) {}
    
          }
    
          // 左右两只筷子未用,则拿起筷子进餐
    
          chops[ph] = true;
    
          chops[(ph + 1) % 5] = true;
    
          eatcount++;
    
          System.out.println( "PH." + ph + "......Eating\tCount = " + eatcount );
    
        } finally {
    
          lock.unlock();
    
        }
    
        try {
    
          Thread.sleep(5);
    
        } catch (InterruptedException e) { /* TODO:处理代码 */}
    
      }
    
     
    
      public void think( int ph) {
    
        lock.lock();
    
        try {
    
          // 哲学家转为思考,则放弃左右两只筷子,并唤醒左右两个哲学家。
    
          chops[ph] = false;
    
          chops[(ph + 1) % 5] = false;
    
          ph_monitor[(ph + 1) % 5].signal();
    
          ph_monitor[(ph + 4) % 5].signal();
    
          eatcount--; // 进餐人数减1
    
          System.out.println( "PH." + ph + "......Thinking\tCount = " + eatcount );
    
        } finally {
    
          lock.unlock();
    
        }
    
        try {
    
          Thread.sleep(5);
    
        } catch (InterruptedException e) { /* TODO:处理代码 */}
    
      }
    
    }
    
     
    
     
    
    class Philosopher implements Runnable {
    
      private int ph = 0;
    
      private Chopsticks ch;
    
      private boolean runflag = true; // 工作标志,false表示哲学家停止工作
    
     
    
      Philosopher(int ph, Chopsticks ch) {
    
        this.ph = ph;
    
        this.ch = ch;
    
      }
    
     
    
      private void work() {
    
        // 哲学家工作,包含进餐和思考,并严格交替执行
    
        ch.eat(ph);
    
        ch.think(ph);
    
      }
    
     
    
      public void run() {
    
        while ( runflag) {
    
          // 判断工作标志,工作标志为true时,不停工作
    
          work();
    
        }
    
      }
    
     
    
      public void stopRun() {
    
        runflag = false;
    
      }
    
    }
    
     
    
     
    
    public class ThreadDpp {
    
      public static void main(String[] args) {
    
        final Chopsticks ch = new Chopsticks();
    
        Philosopher ph[] = new Philosopher[5];
    
        Thread t[] = new Thread[5];
    
        for (int i = 0; i < 5; i++) {
    
          ph[i] = new Philosopher( i, ch);
    
          t[i] = new Thread( ph[ i]);
    
          t[i].start();
    
        }
    
        // 哲学家工作3秒后停止
    
        try {
    
          Thread.sleep(3000);
    
        } catch (InterruptedException e) { /* TODO:处理代码 */}
    
        for (int i = 0; i < 5; i++) {
    
          ph[i].stopRun();
    
        }
    
      }
    
    }


运行结果(部分)


PH.0......Eating   Count = 1


PH.1 Waiting...


PH.2......Eating   Count = 2


PH.3 Waiting...


PH.4 Waiting...


PH.2......Thinking Count = 1


PH.3......Eating   Count = 2


PH.1 Waiting...


PH.0......Thinking Count = 1


PH.1......Eating   Count = 2


PH.4 Waiting...


PH.3......Thinking Count = 1


PH.0 Waiting...


PH.4......Eating   Count = 2


PH.1......Thinking Count = 1


PH.2......Eating   Count = 2


PH.0 Waiting...


PH.3 Waiting...


PH.2......Thinking Count = 1


PH.4......Thinking Count = 0