生产消费模型及其重要性

生产者消费者模型的概念

        生产者消费者模型 就是通过一个容器来解决生产者和消费者的强耦合问题。

        生产者和消费者彼此之间不直接通讯,而通过容器来通讯,所以生产者生产完数据之后不用等待消费者处理,直接将生产的数据放到这个容器当中;消费者也不用找生产者索要数据,而是直接从这个容器中取数据。容器就类似于一个缓冲区,平衡了生产者和消费者的处理能力,这个容器完成了生产者和消费者之间的解耦。

         如果没有使用生产者消费者模型时,生产者和消费者之间直接相互联通,两者之间强耦合,若是一方更换,那另一方也需要随之更换,那样是十分不可取的。

生产者和消费者之间的原则——“321原则”

        一般的生产消费模型都遵循该原则。

三种关系:生产者之间互斥,消费者之间互斥,生产者和消费者之间互斥和同步关系
两种角色:生产者线程和消费者线程
一个交易场所:一个特定结构的缓冲区

        生产者和生产者、消费者和消费者、生产者和消费者,它们之间为什么会存在互斥关系?

        介于生产者和消费者之间的容器可能会被多个执行流同时访问,因此需要将该临界资源用互斥锁保护起来。所以所有生产者和消费者都会竞争式的申请锁,因此生产者和生产者、消费者和消费者、生产者和消费者之间都存在互斥关系。

        生产者和消费者之间为什么会存在同步关系?

        若一直让生产者生产,那么当生产者生产的数据装满容器后,生产者再生产数据就会生产失败。反之,让消费者一直消费,那么当容器当中的数据被消费完后,消费者再进行消费就会消费失败。虽然这样不会造成任何数据不一致的问题,但是这样会引起另一方的饥饿问题,是非常低效的。应该让生产者和消费者访问该容器时具有一定的顺序性,比如让生产者先生产,然后再让消费者进行消费。

生产消费模型一般有两种特点:解耦合、削峰填谷

        在一般的工作场景中,有时入口服务器会出现请求暴涨的情况,但是由于入口服务器的请求计算量不大,即便计算量暴涨也不会导致服务器挂掉,但是作为响应服务器的一方的计算量很大,在没有生产消费者模型的情况下,会导致响应服务器崩溃。

扫描二维码关注公众号,回复: 17286196 查看本文章

       而若是有生产消费模型作为中间容器,当入口服务器出现很多请求,阻塞队列会存储所有请求,而响应服务器则不需要一瞬间全部响应,而是维持原来的速度处理服务器,从而防止服务器崩溃。——削峰

        像这种一瞬间出现大量请求的情况不会持续很长,等峰值过去,响应服务器不会因此减缓速度,而是按原来速度处理队列任务。——填谷

        代码实现。创建一个Manager主函数类——共享队列对象

package DuiLeiMoXin;

import java.util.ArrayList;

public class Manager {
    public static void main(String[] args) {
        //共享队列对象
        ArrayList<Shape> que = new ArrayList();
        //创建生产、消费线程类的对象
        ThreadProduce tp = new ThreadProduce(que);
        tp.start();

        ThreadCusotmer tc = new ThreadCusotmer(que);
        tc.start();
    }
}

Shape类——放入生产消费队列中的对象类,相当于容器

package DuiLeiMoXin;

//放入生产消费队列中的对象类
public class Shape {

    public int id;
    public int x;
    public int y;

    //构造器
    public Shape(int id){
        this.id=id;
    }

    public String toString(){
        return " id "+id+" x "+x+" y "+y;
    }

    public void draw(Object o) {
    }
}

ThreadCusotmer类——模拟消费线程

package DuiLeiMoXin;

import java.util.ArrayList;

//模拟消费线程
public class ThreadCusotmer extends Thread{

    private ArrayList<Shape> queue; //指向共享的队列

    public ThreadCusotmer(ArrayList<Shape> queue){
        this.queue=queue;
    }

    public void run(){
        while (true){
            //如果队列中没有,则放入
            if (this.queue.size()>0){
                Shape sp = this.queue.remove(0);
                System.out.println("<---取出一个"+ sp);
            }
            try {
                Thread.sleep(5);
            }catch (Exception ef){}
        }
    }
}

ThreadProduce类——模拟生产线程

package DuiLeiMoXin;

import java.util.ArrayList;
import java.util.Random;

//模拟生产线程
public class ThreadProduce extends Thread{
    private ArrayList<Shape> queue; //指向共享的队列
    public ThreadProduce(ArrayList<Shape> queue){
        this.queue=queue;
    }
    public void run(){
        Random ran = new Random();
        int count = 0;
        while (true){
            //如果队列中没有,则放入
            if (this.queue.size()==0){
                Shape sp = new Shape(count++);
//                创建Random类型的引用:Random ran = new Random();
//                使用功能:ran.nextInt() 产生int返回内的随机整数 ran.nextInt(整数n) 产生[0,n)范围内的int类型的随机整数
                sp.x = ran.nextInt(600);
                sp.y = ran.nextInt(800);
                //存入到队列中
                this.queue.add(sp);
                System.out.println("--->存入一个"+ sp);
            }
            try {
                Thread.sleep(5);
            }catch (Exception ef){}
        }
    }
}

        生产线程中创建随机数,x、y随机取0-600、0-800的随机整数,存入队列中。消费线程取出生产线程放入队列的数据。

        将生产消费模型运用到飞机大战中,ThreadDraw就是生产者、ThreadMove就是消费者,将子弹 画在Shape队列中,消费者控制子弹的移动。

package FeiJiDaZhan3_1;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;


//用一个内存队列,来保存要画的对象
//一个线程,来画
public class Game extends JFrame {


    //队列对象
    private ArrayList<Shape>  al=new ArrayList();
    private int count ;
    public void initUI(){
        this.setSize(600,800);
        this.setDefaultCloseOperation(3);
        FlowLayout fl=new FlowLayout();
        this.setLayout(fl);
        this.setVisible(true);

        JButton buSend=new JButton("发射");
        this.add(buSend);

        Graphics g=this.getGraphics();
        //分别启动绘制和移动线程
        ThreadMove tm = new ThreadMove(al);
        tm.start();
        ThreadDraw td = new ThreadDraw(al,g);
        td.start();

        buSend.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                count++;
                Shape sp = new Shape(count);
                Random ran = new Random();
                sp.x=ran.nextInt(800);
                sp.y=ran.nextInt(800);
                al.add(sp);
            }
        });
    }

    public static void main(String args[]) {
        Game wu=new Game();
        wu.initUI();
    }
}
package FeiJiDaZhan3_1;

import java.awt.*;

//放入生产消费队列中的对象类
//OOP:封装,多态,继承
// 面向对象五大原则:单一职责原则、开放封闭原则、里氏替换原则、接口分离原则、依赖倒置原则

public class Shape {
    private int count;
    public int id;
    public  int x;
    public  int y;

    public Shape(int count) {
        this.count=count;
    }
    //在界面上画出来
    public void draw(Graphics g){
        g.fillOval(x,y,10,10);
    }
    public void move() {
        this.y++;
    }
}

package FeiJiDaZhan3_1;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

//模拟生产线程
public class ThreadDraw extends Thread{

    private  Graphics g;
    private ArrayList<Shape> queue; //指向共享的队列

    public ThreadDraw(ArrayList<Shape> queue, Graphics g){
        this.g =g;
        this.queue=queue;
    }

    public void run(){

        while (true){
            //画到缓冲区上
            BufferedImage bi = new BufferedImage(600,800,BufferedImage.TYPE_INT_RGB);
            Graphics gi = bi.getGraphics();

            for (int i=0;i<queue.size();i++){
                Shape sp = queue.get(i);
                sp.draw(gi);
            }
            //在画到真正的画布上
            g.drawImage(bi,0,70,null);

            try {
                Thread.sleep(10);
            }catch (Exception ef){}
        }
    }
}
package FeiJiDaZhan3_1;

import java.util.ArrayList;

//模拟消费线程
public class ThreadMove extends Thread{

    private ArrayList<Shape> queue; //指向共享的队列

    public ThreadMove(ArrayList<Shape> queue){
        this.queue=queue;
    }

    public void run(){
        while (true){

            for (int i = 0;i<queue.size();i++){
                Shape sp = queue.get(i);
                sp.move();
            }

            try {
                Thread.sleep(10);
            }catch (Exception ef){}
        }
    }
}

找一些素材图片 替换子弹

        在Shape类中,添加这两条语句

public ImageIcon ic =  new ImageIcon("zidan.png");
g.drawImage(ic.getImage(), x,y,null);

猜你喜欢

转载自blog.csdn.net/weixin_65240122/article/details/132324013