生产者消费者模型的概念
生产者消费者模型 就是通过一个容器来解决生产者和消费者的强耦合问题。
生产者和消费者彼此之间不直接通讯,而通过容器来通讯,所以生产者生产完数据之后不用等待消费者处理,直接将生产的数据放到这个容器当中;消费者也不用找生产者索要数据,而是直接从这个容器中取数据。容器就类似于一个缓冲区,平衡了生产者和消费者的处理能力,这个容器完成了生产者和消费者之间的解耦。
如果没有使用生产者消费者模型时,生产者和消费者之间直接相互联通,两者之间强耦合,若是一方更换,那另一方也需要随之更换,那样是十分不可取的。
生产者和消费者之间的原则——“321原则”
一般的生产消费模型都遵循该原则。
三种关系:生产者之间互斥,消费者之间互斥,生产者和消费者之间互斥和同步关系
两种角色:生产者线程和消费者线程
一个交易场所:一个特定结构的缓冲区
生产者和生产者、消费者和消费者、生产者和消费者,它们之间为什么会存在互斥关系?
介于生产者和消费者之间的容器可能会被多个执行流同时访问,因此需要将该临界资源用互斥锁保护起来。所以所有生产者和消费者都会竞争式的申请锁,因此生产者和生产者、消费者和消费者、生产者和消费者之间都存在互斥关系。
生产者和消费者之间为什么会存在同步关系?
若一直让生产者生产,那么当生产者生产的数据装满容器后,生产者再生产数据就会生产失败。反之,让消费者一直消费,那么当容器当中的数据被消费完后,消费者再进行消费就会消费失败。虽然这样不会造成任何数据不一致的问题,但是这样会引起另一方的饥饿问题,是非常低效的。应该让生产者和消费者访问该容器时具有一定的顺序性,比如让生产者先生产,然后再让消费者进行消费。
生产消费模型一般有两种特点:解耦合、削峰填谷
在一般的工作场景中,有时入口服务器会出现请求暴涨的情况,但是由于入口服务器的请求计算量不大,即便计算量暴涨也不会导致服务器挂掉,但是作为响应服务器的一方的计算量很大,在没有生产消费者模型的情况下,会导致响应服务器崩溃。
而若是有生产消费模型作为中间容器,当入口服务器出现很多请求,阻塞队列会存储所有请求,而响应服务器则不需要一瞬间全部响应,而是维持原来的速度处理服务器,从而防止服务器崩溃。——削峰
像这种一瞬间出现大量请求的情况不会持续很长,等峰值过去,响应服务器不会因此减缓速度,而是按原来速度处理队列任务。——填谷
代码实现。创建一个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);