ITPub博客

首页 > 数据库 > 数据库开发技术 > 解密!设计模式之生产者与消费者

解密!设计模式之生产者与消费者

原创 数据库开发技术 作者:Finally_m 时间:2023-04-07 08:00:00 0 删除 编辑

关注Java架构栈,干货天天都不断!



 前言


本文主要讲述生产者和消费者模式,文中会使用通俗易懂的案例,使你更好地学习本章知识点并理解原理,做到有道无术。


 什么是生产者和消费者模式


生产者消费者模式其实并不属于常见的23种设计模式之一,它是Controlnet网络中特有的一种传输数据模式,设置方便,使用安全快捷。生产者消费者模式,其实是一种通过容器解决生产者和消费者之间强耦合问题的设计模式。


 生活中的生产者和消费者模式


在生活中,有很多店铺的经营方式跟生产者和消费者模式其实很相似,比如以下这些场景。


1.蛋糕店

在飘香四溢的蛋糕店中,我们能看到各式各样诱人的面包或者蛋糕,摆放在柜台中等待客户购买。江帅比较喜欢黑森林蛋糕和肠仔包,不知道大家喜欢哪些面包和蛋糕。


这些面包和蛋糕都是由糕点师在厨房中制造出来的,然后摆放在柜台中。因柜台的容量有限,且消费者的消费量也有限等原因,糕点师不会无限地再制作面包和蛋糕。当柜台摆满后,糕点师就不会再继续制作了;而当柜台已经空了或者即将要空了,糕点师就会继续制作。一般情况下,糕点师不会与购物的客户直接对接。这样的模式,其实就跟设计模式中的生产者和消费者类似。



2.水果店

我们知道,每个人平时都需要适当地进食一些水果,因为水果中含丰富的维生素和维他命,是咱们人体所需的。而喜欢吃水果的人也可以分为2类,一类是特别喜欢吃榴莲的,一类是受不了榴莲味不吃榴莲的,江帅则属于后者,你们是属于那种呢。


大部分人买水果都会去水果店或者超市,水果由果农生产出来,经过一系列途径进入到水果店或者超市中。当水果滞销时,店家会减少进货甚至不进货;而当水果热销短缺时,店家则会进货。生产水果的果农并不与购买水果的客户直接对接,这样的模式也会跟设计模式中的生产者和消费者类似。



3.烧烤店

在炎热的夏天夜里,撸点串喝点小酒是对夏天的尊重,这是江帅在夏天喜欢做的事情。

在烧烤店中,老板需要提前准备好食材,如果消费人员多,食材不够,老板则会多准备食材;如果消费人员少,老板则会少准备食材。出售食材的商贩并不与到烧烤店消费的客户直接对接,这样的模式就跟设计模式中的生产者和消费者类似。



 生产者和消费者模式的代码实现


接下来江帅就以蛋糕店举例,通过生产者和消费者模式来实现,此案例会建立在多线程的基础上。首先我会创建一个产品类蛋糕类,和一个缓冲区蛋糕店类,代码如下所示:

package com.qianfeng.ran;
/*
 * @author:江帅
 * 产品类
 * 蛋糕类
 */
public class Cake {
    //蛋糕名
    private String name;

    public Cake() {
    }

    public Cake(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Cake{" +
                "name='" + name + '\'' +
                '
}';
    }
}

/*
 * @author:江帅
 * 缓冲区
 * 蛋糕店
 */
class CakeShop {
    //存放蛋糕的柜台   --  存放蛋糕类对象的数组
    private Cake[] counter = new Cake[10];
    //已有蛋糕的数量   --  指针
    private int index = 0;

    //厨师把蛋糕放在柜台的行为  --  生产者存放产品的方法
    public synchronized void input(Cake cake){
        //判断柜台是否满了  --  判断数组是否已满
        while(index == counter.length){
            try {
                //让糕点师暂停制作蛋糕 --  使生产者线程进入等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //把蛋糕放在柜台上  --  把产品存放在数组中
        counter[index++] = cake;
        System.out.println(Thread.currentThread().getName()+"大厨制作了一个"+cake.getName()+",柜台上有"+index+"个蛋糕");
        //通知客人可以继续消费    --  唤醒消费者继续消费
        this.notifyAll();
    }

    //客户购买蛋糕的行为     --  消费者消费产品的方法
    public synchronized void outPut(){
        //判断柜台是否为空
        while (index == 0){
            try {
                //客户停止消费    --  使消费者线程进入等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //客户购买的蛋糕
        Cake cake = counter[--index];
        System.out.println(Thread.currentThread().getName()+"购买了一个"+cake.getName());
        //已购买的蛋糕所在柜台的位置为null
        counter[index] = null;
        //提醒所有的糕点师该继续继续制作蛋糕      --  唤醒所有的生产者
        this.notifyAll();
    }
}


再创建生产者类和消费者类,这两个类都会实现Runnable接口。

package com.qianfeng.ran;
import java.util.Random;

/*
 * @author:江帅
 * 生产者
 * 糕点师
 */
public class PastryCook implements Runnable {
    //蛋糕店对象
    private CakeShop cakeShop;
    public PastryCook(CakeShop cakeShop){
        this.cakeShop = cakeShop;
    }

    @Override
    public void run() {
        while (true){
            //制作蛋糕        --        创建蛋糕类对象
            Cake cake = makeCake();
            //存放到柜台中
            cakeShop.input(cake);
            try {
                //适当休眠
                Thread.sleep(new Random().nextInt(2000)+1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     *  制作蛋糕            --  创建随机的蛋糕
     * @return  蛋糕类对象
     */
    private Cake makeCake() {
        String[] cakeNames = {"黑森林蛋糕","抹茶蛋糕","草莓蛋糕","水果蛋糕","青草蛋糕"};
        //随机生成蛋糕名
        String cakeName = cakeNames[(int)(Math.random()*cakeNames.length)];
        //创建蛋糕类对象并返回
        return new Cake(cakeName);
    }
}

/*
 * @author:江帅
 * 消费者
 * 人类
 */
class Human implements Runnable{
    //蛋糕店对象
    private CakeShop cakeShop;
    public Human(CakeShop cakeShop){
        this.cakeShop = cakeShop;
    }

    @Override
    public void run() {
        while(true){
            //客户购买蛋糕行为
            cakeShop.outPut();
            try {
                //适当休眠
                Thread.sleep(new Random().nextInt(3000)+2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


最后通过生产者和消费者模式,来模拟用户消费和厨师生产的场景。

package com.qianfeng.ran;

/*
 * @author:江帅
 * 客户端
 */
public class Demo {
    public static void main(String[] args) {
        //----------打印结果为糕点师生成蛋糕、消费者购买蛋糕的场景-------
        //---------------当蛋糕达到10个,糕点师不会继续生产------------
        //-----------------当蛋糕售空后,消费者不会再购买--------------
        //创建蛋糕店
        CakeShop cakeShop = new CakeShop();
        //创建多个糕点师并开始在蛋糕店制作蛋糕
        PastryCook pastryCook = new PastryCook(cakeShop);
        new Thread(pastryCook,"小当家").start();
        new Thread(pastryCook,"南翔").start();

        //创建多个消费者并开始在蛋糕店购买蛋糕
        Human human = new Human(cakeShop);
        new Thread(human,"江帅").start();
        new Thread(human,"Jack Ma").start();
        new Thread(human,"企鹅马").start();
    }
}


 总结


最后江帅给大家总结一下本文的重点内容:

  • 生成者把生成的数据存放到缓冲区里,当缓冲区不为空时需要通知消费者消费,当缓冲区满了则自己进入等待。

  • 消费者者需要数据时则从缓冲区中获取,当缓冲区不为空时需要通知生产者生成,当缓冲区满了则自己进入等待。

  • 在生产者与消费者模式中,生产者并不与消费者产生直接的联系。而是通过缓冲区把生产者(糕点师)和消费者(客户)进行关联,使生产者和消费者的耦合度降低,达到解耦性。


在下一篇文章中,江帅会带大家学习钢铁侠的形成(设计模式之装饰者模式),欢迎继续关注哦。



- END -


近期课程上新:
Java基础 | Javaweb基础 | Java高级框架 | 微服务架构 | TiDB分布式数据库入门最佳实践 | VUE全面剖析及前后端联动实战 | 23种设计模式 | 企业级项目实战 | Java面试宝典

原文作者:Java架构栈;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-04-13

  • 博文量
    573
  • 访问量
    243785