← 返回首页
JavaSE系列教程(六十五)
发表时间:2020-02-11 16:06:39
讲解生产者与消费者。

1.什么是生产者与消费者

生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。生产者生成一定量的数据放到缓冲区中,然后重复此过程;与此同时,消费者也在缓冲区消耗这些数据。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。不够完善的解决方法容易出现死锁的情况,此时进程都在等待唤醒。

生产者与消费者示意图如下:

问题抛出如下: 妈妈负责做馒头,大林和小林负责吃馒头,厨房有个锅,锅最多放10个馒头,妈妈一共做100个馒头就结束。 使用多线程实现生产者与消费者的多线程例子。统计大林和小林各吃了多少个馒头?

通过分析得知:妈妈线程是生产者线程,大林和小林是消费者线程。锅属于临界资源(缓冲区)。

实现代码如下:


//锅类
/*
* 有限缓冲区
* Stack描述锅。后进先出
*
* */
public class Pot {

    private Stack<Integer> pot = new Stack<Integer>();

    public static final int MAX_LEN = 10; //锅最多只能放10个馒头。
    public static final int MAX_NUMBER =100; //妈妈一共做100个馒头,程序就结束了。
    public static int COUNT = 0; //统计现在做了多少个馒头。
    private int bigSonNum; //大儿子吃的数量
    private int smallSonNum; //小儿子吃的数量

    public Stack<Integer> getPot() {
        return pot;
    }

    //做馒头的方法
    public synchronized void makeCake(){

        COUNT++;
        pot.push(COUNT);
        System.out.println("妈妈做了第" + COUNT + "个馒头....");
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //吃馒头的方法
    public synchronized  void eatCake(String name){
        int temp = pot.pop(); //弹出一个馒头。
        System.out.println(name+"吃了第" + temp + "个馒头...");

        if("大林".equals(name)){
            this.bigSonNum++;
        }else{
            this.smallSonNum++;
        }

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    //显示结果
    public void showResult(){
        System.out.println("大林吃了:" + bigSonNum + "个馒头!");
        System.out.println("小林吃了:" + smallSonNum+"个馒头!");
    }

}


//生产者,妈妈类
public class Producer implements Runnable {

    private Pot pot;

    public Producer(Pot pot) {
        this.pot = pot;
    }

    @Override
    public void run() {
        while(true){
          synchronized (pot) {
              //不停的做馒头。。。
              //判断什么时候就不做了。
              if (Pot.COUNT >= 100) {
                  break;
              }
              //怎么判断锅填满了呢。
              if (pot.getPot().size() == Pot.MAX_LEN) {
                  //妈妈就进入等待队列了,等待儿子吃馒头。
                  System.out.println("锅里馒头满了,等待儿子吃馒头....");
                  try {
                      //可以换线儿子吃馒头。
                      pot.notifyAll();
                      pot.wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              } else {
                  //可以换线儿子吃馒头。
                  pot.notifyAll();
                  pot.makeCake();
              }
          }

        }
    }
}

//消费者类,儿子类
public class Consumer implements Runnable {

    private Pot pot;
    private String name;


    public Consumer(Pot pot,String name) {
        this.pot = pot;
        this.name = name;
    }

    @Override
    public void run() {
        while(true){

            synchronized (pot) {
                //儿子不停的吃馒头。
                //判断什么时候不能了。
                if (pot.getPot().size() == 0 && Pot.COUNT >= 100) {
                    break;
                }

                //判断锅里有馒头吗?
                if (pot.getPot().size() == 0) {
                    //妈妈就进入等待队列了,等待儿子吃馒头。
                    System.out.println("锅里没有馒头了,等待妈妈做馒头....");
                    try {
                        //可以换醒妈妈做吃馒头。
                        pot.notifyAll();
                        //pot.notify();
                        pot.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    pot.eatCake(this.name);
                    pot.notifyAll();
                }
            }
        }
    }
}

public class Test {


    public static void main(String[] args) {

        Pot pot = new Pot();//创建锅对象,有限缓冲区。

        Producer mother = new Producer(pot);
        Consumer bigSon = new Consumer(pot,"大林");
        Consumer smallSon = new Consumer(pot,"小林");

        Thread motherThread = new Thread(mother);
        Thread bigSonThread = new Thread(bigSon);
        Thread smallSonThread = new Thread(smallSon);

        motherThread.start();
        bigSonThread.start();
        smallSonThread.start();

        try{
            motherThread.join(); //等待妈妈线程结束
            bigSonThread.join(); //等待大儿子线程结束
            smallSonThread.join(); //等待小儿子线程结束
        }catch(Exception ex){
            ex.printStackTrace();
        }
        pot.showResult();

    }
}

执行结果:
妈妈做了第1个馒头....
妈妈做了第2个馒头....
妈妈做了第3个馒头....
妈妈做了第4个馒头....
妈妈做了第5个馒头....
妈妈做了第6个馒头....
妈妈做了第7个馒头....
妈妈做了第8个馒头....
妈妈做了第9个馒头....
妈妈做了第10个馒头....
锅里馒头满了,等待儿子吃馒头....
小林吃了第10个馒头...
小林吃了第9个馒头...
小林吃了第8个馒头...
小林吃了第7个馒头...
小林吃了第6个馒头...
小林吃了第5个馒头...
小林吃了第4个馒头...
小林吃了第3个馒头...
小林吃了第2个馒头...
小林吃了第1个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第11个馒头....
妈妈做了第12个馒头....
妈妈做了第13个馒头....
妈妈做了第14个馒头....
妈妈做了第15个馒头....
妈妈做了第16个馒头....
妈妈做了第17个馒头....
妈妈做了第18个馒头....
妈妈做了第19个馒头....
妈妈做了第20个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第20个馒头...
大林吃了第19个馒头...
大林吃了第18个馒头...
大林吃了第17个馒头...
大林吃了第16个馒头...
大林吃了第15个馒头...
大林吃了第14个馒头...
大林吃了第13个馒头...
大林吃了第12个馒头...
大林吃了第11个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第21个馒头....
妈妈做了第22个馒头....
妈妈做了第23个馒头....
妈妈做了第24个馒头....
妈妈做了第25个馒头....
妈妈做了第26个馒头....
妈妈做了第27个馒头....
妈妈做了第28个馒头....
妈妈做了第29个馒头....
妈妈做了第30个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第30个馒头...
大林吃了第29个馒头...
大林吃了第28个馒头...
大林吃了第27个馒头...
大林吃了第26个馒头...
大林吃了第25个馒头...
大林吃了第24个馒头...
大林吃了第23个馒头...
大林吃了第22个馒头...
大林吃了第21个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第31个馒头....
妈妈做了第32个馒头....
妈妈做了第33个馒头....
妈妈做了第34个馒头....
妈妈做了第35个馒头....
妈妈做了第36个馒头....
妈妈做了第37个馒头....
妈妈做了第38个馒头....
妈妈做了第39个馒头....
妈妈做了第40个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第40个馒头...
大林吃了第39个馒头...
大林吃了第38个馒头...
大林吃了第37个馒头...
大林吃了第36个馒头...
大林吃了第35个馒头...
大林吃了第34个馒头...
大林吃了第33个馒头...
大林吃了第32个馒头...
大林吃了第31个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第41个馒头....
妈妈做了第42个馒头....
妈妈做了第43个馒头....
妈妈做了第44个馒头....
妈妈做了第45个馒头....
妈妈做了第46个馒头....
妈妈做了第47个馒头....
妈妈做了第48个馒头....
妈妈做了第49个馒头....
妈妈做了第50个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第50个馒头...
大林吃了第49个馒头...
大林吃了第48个馒头...
大林吃了第47个馒头...
大林吃了第46个馒头...
大林吃了第45个馒头...
大林吃了第44个馒头...
大林吃了第43个馒头...
大林吃了第42个馒头...
大林吃了第41个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第51个馒头....
妈妈做了第52个馒头....
妈妈做了第53个馒头....
妈妈做了第54个馒头....
妈妈做了第55个馒头....
妈妈做了第56个馒头....
妈妈做了第57个馒头....
妈妈做了第58个馒头....
妈妈做了第59个馒头....
妈妈做了第60个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第60个馒头...
大林吃了第59个馒头...
大林吃了第58个馒头...
大林吃了第57个馒头...
大林吃了第56个馒头...
大林吃了第55个馒头...
大林吃了第54个馒头...
大林吃了第53个馒头...
大林吃了第52个馒头...
大林吃了第51个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第61个馒头....
妈妈做了第62个馒头....
妈妈做了第63个馒头....
妈妈做了第64个馒头....
妈妈做了第65个馒头....
妈妈做了第66个馒头....
妈妈做了第67个馒头....
妈妈做了第68个馒头....
妈妈做了第69个馒头....
妈妈做了第70个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第70个馒头...
大林吃了第69个馒头...
大林吃了第68个馒头...
大林吃了第67个馒头...
大林吃了第66个馒头...
大林吃了第65个馒头...
大林吃了第64个馒头...
大林吃了第63个馒头...
大林吃了第62个馒头...
大林吃了第61个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第71个馒头....
妈妈做了第72个馒头....
妈妈做了第73个馒头....
妈妈做了第74个馒头....
妈妈做了第75个馒头....
妈妈做了第76个馒头....
妈妈做了第77个馒头....
妈妈做了第78个馒头....
妈妈做了第79个馒头....
妈妈做了第80个馒头....
锅里馒头满了,等待儿子吃馒头....
小林吃了第80个馒头...
小林吃了第79个馒头...
小林吃了第78个馒头...
小林吃了第77个馒头...
小林吃了第76个馒头...
小林吃了第75个馒头...
小林吃了第74个馒头...
小林吃了第73个馒头...
小林吃了第72个馒头...
小林吃了第71个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第81个馒头....
妈妈做了第82个馒头....
妈妈做了第83个馒头....
妈妈做了第84个馒头....
妈妈做了第85个馒头....
妈妈做了第86个馒头....
妈妈做了第87个馒头....
妈妈做了第88个馒头....
妈妈做了第89个馒头....
妈妈做了第90个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第90个馒头...
大林吃了第89个馒头...
大林吃了第88个馒头...
大林吃了第87个馒头...
大林吃了第86个馒头...
大林吃了第85个馒头...
大林吃了第84个馒头...
大林吃了第83个馒头...
大林吃了第82个馒头...
大林吃了第81个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第91个馒头....
妈妈做了第92个馒头....
妈妈做了第93个馒头....
妈妈做了第94个馒头....
妈妈做了第95个馒头....
妈妈做了第96个馒头....
妈妈做了第97个馒头....
妈妈做了第98个馒头....
妈妈做了第99个馒头....
妈妈做了第100个馒头....
大林吃了第100个馒头...
大林吃了第99个馒头...
大林吃了第98个馒头...
大林吃了第97个馒头...
大林吃了第96个馒头...
大林吃了第95个馒头...
大林吃了第94个馒头...
大林吃了第93个馒头...
大林吃了第92个馒头...
大林吃了第91个馒头...
大林吃了:80个馒头!
小林吃了:20个馒头!

2.生产者与消费者的多种实现方式

1)wait() / notify()方法 上例中我们就是使用了wait() / notify()方式实现。

2)await() / signal()方法

3)BlockingQueue阻塞队列方法

4)信号量

5)管道