Java多线程详解之四:生产者消费者问题

一、问题描述

  生产者消费者问题(Producer-Consumer problem),也称有限缓冲区问题(Bounded-buffer promblem),是一个多线程同步问题的经典案例。对于一个固定大小的缓冲区,有两个线程共享该缓冲区----即“生产者”和“消费者”。顾名思义,“生产者”就是往缓冲区中添加数据的线程,“消费者”就是消耗缓冲区中数据的线程。如果不加以限制,当缓冲区满了的时候,如果“生产者”继续生产,就会导致缓冲区溢出;当缓冲区为空时,“消费者”也无法消费。

  一个栗子:就拿北京烤鸭来说,烤鸭子的厨师就是“生产者”,买烤鸭的顾客就是“消费者”,烤鸭子的炉子就是有限缓冲区,比如说一个炉子只能放10只鸭子,那这个缓冲区的大小就是10,鸭子就是缓冲区中的数据。当炉子里已经有10只鸭子的时候,厨师就不能再添加烤鸭了,没地方放了,就必须等待顾客来买鸭子;而当炉子里没有鸭子时,顾客就必须等待厨师烤鸭子。

 

二、问题核心

所以该问题主要有以下几点需要注意:

(1)在一个线程进行生产或者消费时,其余线程就不能再进行消费或者生产(线程同步)

 

 

(2)在缓冲区为满时,生产者就不能继续生产

 

 

(3)在缓冲区为空时,消费者就不能继续消费

 

 

三、Java代码实现

  • 当缓冲区已满时,生产者线程停止执行,放弃锁,使自己处于等状态,让其他线程执行;
  • 当缓冲区已空时,消费者线程停止执行,放弃锁,使自己处于等状态,让其他线程执行;
  • 当生产者向缓冲区放入一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态;
  • 当消费者从缓冲区取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态;

 

产品Duck.java

//产品类
class Duck{
    //产品编号
    public int id;

    public Duck(int id) {
        this.id = id;
    }
}

 

 缓冲区Container.java

//缓冲区类
class Container{
    //容器大小为10
    Duck[] ducks = new Duck[10];
    //记录当前缓冲区数据量
    int count = 0;

    //生产者生产
    public synchronized void push(Duck duck) throws InterruptedException {
        //如果满了,就需要等待消费者
        if(count == 10){
            this.wait();    //生产者等待
            this.notifyAll();  //通知消费者消费
        }

        //如果没满,就将生产的产品放入缓冲区
        System.out.println("正在生产第"+duck.id+"只烤鸭");
        ducks[count++] = duck;
        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费
    public synchronized Duck pop() throws InterruptedException {
        //如果为空,就等生产者
        if(count == 0){
            this.wait();  //消费者等待
            this.notifyAll(); //唤醒消费者
        }
        //如果不空就消费
        count--;
        Duck duck = ducks[count];
        System.out.println("正在消费第"+duck.id+"只烤鸭");
        //通知生产者生产
        this.notifyAll();
        return duck;
    }
}

 

生产者Producer.java

class Producer extends Thread{
    private Container container;   //缓冲区

    public Producer(Container container){
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            Duck duck = new Duck(i);
            try {
                Thread.sleep(10);
                container.push(duck);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

消费者Consumer.java

class Consumer extends Thread{
    private Container container;   //缓冲区

    public Consumer(Container container){
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(100);
                Duck duck = container.pop();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

 

主方法:

public static void main(String[] args) {
        //创建一个缓冲区对象
        Container container = new Container();
        //创建生产者,消费对象
        Producer producer = new Producer(container);   //生产者
        Consumer consumer = new Consumer(container);

        //开启线程
        producer.start();
        consumer.start();
}

 

 注:wait()表示线程一直等待,直到其他线程唤醒该线程,并且该线程会释放对象得锁;

  notifyAll()是唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度;

 

 


 

欢迎关注微信公众号,不定期分享学习笔记与资料,与Mike一起学java,谢谢~

posted @ 2020-12-03 18:10  Mike小菜鸡  阅读(521)  评论(0)    收藏  举报