Java实现排队论的原理
作者:xiaojimanman 发布时间:2023-11-23 02:19:24
引入:
前段时间去银行办业务,排队的人那是真多,自己正式办理业务也就不到5分钟,但是却足足等了两个小时(相信很多人都遇到过这种情况),对这种服务水平真的是无语了,但是问题又来了,银行应该开几个窗口,既能保证整体的服务质量,又能保证资源资源的利用率呢?下面我们就通过排队论来模拟这个问题。
排队论简介
排队论是研究系统随机聚散现象和随机系统工作工程的数学理论和方法,又称随机服务系统理论,为运筹学的一个分支。我们下面对排队论做下简化处理,先看下图:
我们在图的左侧安排若干个蓝色服务台,右侧为可能会过来的红色顾客,中间为黄色的等候区,如果有服务台处于空闲状态,顾客可以直接去接受服务,否则就要在黄色区域等候,顾客服务的顺序采用先到现服务的原则,现在如果我们知道顾客过来的概率分布,那么我们在左侧安排几个服务台既能达到更好的服务水平,又能保证服务台的使用率?下面我们就构建模型来模拟这个问题。
排队论分步实现
1)对于排队论,我们首先要确定顾客属性,知道顾客什么时候到达,需要的服务耗时等,我们首先创建一个顾客类,在这里我们指定了顾客服务的最大、最小时间,这里我们为了简化就直接认为服务时间完全随机:
public class CustomerBean {
//最小服务时间
private static int minServeTime = 3 * 1000;
//最大服务时间
private static int maxServeTime = 15 * 1000;
//顾客达到时间
private long arriveTime;
//顾客需要服务耗时
private int serveTime;
public CustomerBean() {
//设置到达时间
arriveTime = System.currentTimeMillis();
//随机设置顾客的服务时间
serveTime = (int) (Math.random() * (maxServeTime - minServeTime) + minServeTime);
}
}
2)上面我们定义了顾客,紧接着就需要定义一个排队队列,我们先看下队列的属性,这里我们定义一个数组,用它来保存排队的顾客,定义下一个顾客到来的最小、最大时间间隔以及顾客来不来的概率(这里简单说明下,如果下一个顾客的间隔时间是3,但是通过概率计算并为满足,则这个顾客不进入队列,这样设置的原因是尽可能的使顾客达到有很大的随机性)和队列中最大的排队人数。
public class CustomerQuene {
//等待顾客队列
private LinkedList<CustomerBean> customers = new LinkedList<CustomerBean>();
//下一个顾客过来最短时间
private int minTime = 0;
//下一个顾客过来最大时间
private int maxTime = 1 * 1000;
//来顾客的概率
private double rate = 0.9;
//标识是否继续产生顾客
private boolean flag = true;
//最大排队人数
private int maxWaitNum = 0;
}
3)顾客和排队的队列都有了,我们就设置一个产生顾客的线程,让它不断的产生顾客,这里就有我们上面说的时间和概率分布。
/**
*@Description: 生成顾客线程
*@Version:1.1.0
*/
private class CustomerThread extends Thread {
private CustomerThread(String name) {
super(name);
}
@Override
public void run() {
while (flag) {
//队尾添加一个新顾客
if (Math.random() < rate) {
customers.addLast(new CustomerBean());
if (maxWaitNum < customers.size()) {
maxWaitNum = customers.size();
}
}
int sleepTime = (int) (Math.random() * (maxTime - minTime) + minTime);
try {
TimeUnit.MILLISECONDS.sleep(sleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
4)如果队列中有顾客排队切有空闲的服务台,就需要获取队头的顾客去接受服务
public synchronized CustomerBean getCustomerBean() {
if (customers == null || customers.size() < 1) {
return null;
}
return customers.removeFirst();
}
5)顾客相关的属性和方法都已经准备好,下面就设置下服务台相关的属性,这里我们直接把服务台设置成线程,定义一些服务指标,如服务的顾客数目、总等待时间、总服务时间、最大等待时间等。
public class ServantThread extends Thread{
//服务顾客数目
private static int customerNum = 0;
//总等待时间
private static int sumWaitTime = 0;
//总服务时间
private static int sumServeTime = 0;
//最大等待时间
private static int maxWaitTime = 0;
private boolean flag = false;
private String name;
}
6)服务台最主要的工作就是服务顾客,这里我们把服务顾客相关的操作写到线程的run方法中。
public void run() {
flag = true;
while (flag) {
CustomerBean customer = CustomerQuene.getCustomerQuene().getCustomerBean();
//如果顾客线程已经关闭且队列中没有顾客,服务台线程关闭释放
if (customer == null) {
if (!CustomerQuene.getCustomerQuene().isFlag()) {
flag = false;
print();
}
continue;
}
long now = System.currentTimeMillis();
int waitTime = (int) (now - customer.getArriveTime());
//保存最大的等待时间
if (waitTime > maxWaitTime) {
maxWaitTime = waitTime;
}
//睡眠时间为顾客的服务时间,代表这段时间在服务顾客
try {
TimeUnit.MILLISECONDS.sleep(customer.getServeTime());
} catch (Exception e) {
e.printStackTrace();
}
System.err.println(name + " 服务顾客耗时:" + customer.getServeTime() + "ms\t顾客等待:" + waitTime + "ms");
customerNum++;
sumWaitTime += waitTime;
sumServeTime += customer.getServeTime();
}
}
7)最后我们编写一个测试模型,来验证服务水平
/**
*@Description:
*/
package com.lulei.opsearch.quene;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
//开门
System.out.println("开门接客啦!");
boolean flag = true;
CustomerQuene.getCustomerQuene();
long a = System.currentTimeMillis();
int servantNum = 10;
for (int i = 0; i < servantNum; i++) {
ServantThread thread = new ServantThread("服务台" + i);
thread.start();
}
while (flag) {
long b = System.currentTimeMillis();
if (b - a > 1 * 60 * 1000 && flag) {
//关门
flag = false;
CustomerQuene.getCustomerQuene().close();
System.out.println("关门不接客啦!");
}
System.out.println("系统运行时间:" + (b -a) + "ms");
System.out.println("系统空闲时间:" + ((b -a) * servantNum - ServantThread.getSumServeTime()));
ServantThread.print();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行结果
1)运行开始
2)顾客产生线程关闭
3)最后服务水平
通过修改服务台的个数就可以评估在当前的顾客情况下应该设置几个服务台。
完整代码
1)顾客类
/**
*@Description:
*/
package com.lulei.opsearch.quene;
public class CustomerBean {
//最小服务时间
private static int minServeTime = 3 * 1000;
//最大服务时间
private static int maxServeTime = 15 * 1000;
//顾客达到时间
private long arriveTime;
//顾客需要服务耗时
private int serveTime;
public CustomerBean() {
//设置到达时间
arriveTime = System.currentTimeMillis();
//随机设置顾客的服务时间
serveTime = (int) (Math.random() * (maxServeTime - minServeTime) + minServeTime);
}
public static int getMinServeTime() {
return minServeTime;
}
public static void setMinServeTime(int minServeTime) {
CustomerBean.minServeTime = minServeTime;
}
public static int getMaxServeTime() {
return maxServeTime;
}
public static void setMaxServeTime(int maxServeTime) {
CustomerBean.maxServeTime = maxServeTime;
}
public long getArriveTime() {
return arriveTime;
}
public void setArriveTime(long arriveTime) {
this.arriveTime = arriveTime;
}
public int getServeTime() {
return serveTime;
}
public void setServeTime(int serveTime) {
this.serveTime = serveTime;
}
}
2)顾客队列
/**
*@Description:
*/
package com.lulei.opsearch.quene;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
public class CustomerQuene {
//等待顾客队列
private LinkedList<CustomerBean> customers = new LinkedList<CustomerBean>();
//下一个顾客过来最短时间
private int minTime = 0;
//下一个顾客过来最大时间
private int maxTime = 1 * 1000;
//来顾客的概率
private double rate = 0.9;
//标识是否继续产生顾客
private boolean flag = true;
//最大排队人数
private int maxWaitNum = 0;
public int getMaxWaitNum() {
return maxWaitNum;
}
public boolean isFlag() {
return flag;
}
/**
* @return
* @Author:lulei
* @Description: 获取排在队头的顾客
*/
public synchronized CustomerBean getCustomerBean() {
if (customers == null || customers.size() < 1) {
return null;
}
return customers.removeFirst();
}
public void close() {
if (flag) {
flag = false;
}
}
/**
* @return
* @Author:lulei
* @Description: 获取等待顾客数量
*/
public int getWaitCustomerNum() {
return customers.size();
}
/**
*@Description: 生成顾客线程
*@Version:1.1.0
*/
private class CustomerThread extends Thread {
private CustomerThread(String name) {
super(name);
}
@Override
public void run() {
while (flag) {
//队尾添加一个新顾客
if (Math.random() < rate) {
customers.addLast(new CustomerBean());
if (maxWaitNum < customers.size()) {
maxWaitNum = customers.size();
}
}
int sleepTime = (int) (Math.random() * (maxTime - minTime) + minTime);
try {
TimeUnit.MILLISECONDS.sleep(sleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//单例模式开始
private static class CustomerQueneDao {
private static CustomerQuene customerQuene = new CustomerQuene();
}
private CustomerQuene() {
CustomerThread customerThread = new CustomerThread("顾客产生线程");
customerThread.start();
}
public static CustomerQuene getCustomerQuene() {
return CustomerQueneDao.customerQuene;
}
//单例模式结束
public int getMinTime() {
return minTime;
}
public void setMinTime(int minTime) {
this.minTime = minTime;
}
public int getMaxTime() {
return maxTime;
}
public void setMaxTime(int maxTime) {
this.maxTime = maxTime;
}
public double getRate() {
return rate;
}
public void setRate(double rate) {
this.rate = rate;
}
}
3)服务台线程
/**
*@Description:
*/
package com.lulei.opsearch.quene;
import java.util.concurrent.TimeUnit;
import com.lulei.util.ParseUtil;
public class ServantThread extends Thread{
//服务顾客数目
private static int customerNum = 0;
//总等待时间
private static int sumWaitTime = 0;
//总服务时间
private static int sumServeTime = 0;
//最大等待时间
private static int maxWaitTime = 0;
private boolean flag = false;
private String name;
public ServantThread(String name) {
super(name);
this.name = name;
}
public static int getMaxWaitTime() {
return maxWaitTime;
}
public static int getSumServeTime() {
return sumServeTime;
}
@Override
public void run() {
flag = true;
while (flag) {
CustomerBean customer = CustomerQuene.getCustomerQuene().getCustomerBean();
//如果顾客线程已经关闭且队列中没有顾客,服务台线程关闭释放
if (customer == null) {
if (!CustomerQuene.getCustomerQuene().isFlag()) {
flag = false;
print();
}
continue;
}
long now = System.currentTimeMillis();
int waitTime = (int) (now - customer.getArriveTime());
//保存最大的等待时间
if (waitTime > maxWaitTime) {
maxWaitTime = waitTime;
}
//睡眠时间为顾客的服务时间,代表这段时间在服务顾客
try {
TimeUnit.MILLISECONDS.sleep(customer.getServeTime());
} catch (Exception e) {
e.printStackTrace();
}
System.err.println(name + " 服务顾客耗时:" + customer.getServeTime() + "ms\t顾客等待:" + waitTime + "ms");
customerNum++;
sumWaitTime += waitTime;
sumServeTime += customer.getServeTime();
}
}
public static void print() {
if (customerNum > 0) {
System.out.println("--------------------------------------");
System.out.println("服务顾客数目:" + customerNum);
System.out.println("最大等待时间:" + maxWaitTime);
System.out.println("等待顾客数目:" + CustomerQuene.getCustomerQuene().getWaitCustomerNum());
System.out.println("最大等待顾客数目:" + CustomerQuene.getCustomerQuene().getMaxWaitNum());
//输出顾客平均等待时间,保留两位小数
System.out.println("顾客平均等待时间:" + ParseUtil.parseDoubleToDouble((sumWaitTime * 1.0 / customerNum), 2) + "ms");
System.out.println("顾客平均服务时间:" + ParseUtil.parseDoubleToDouble((sumServeTime * 1.0 / customerNum), 2) + "ms");
System.out.println("系统总服务时间:" + sumServeTime + "ms");
}
}
}
4)测试模型
/**
*@Description:
*/
package com.lulei.opsearch.quene;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
//开门
System.out.println("开门接客啦!");
boolean flag = true;
CustomerQuene.getCustomerQuene();
long a = System.currentTimeMillis();
int servantNum = 10;
for (int i = 0; i < servantNum; i++) {
ServantThread thread = new ServantThread("服务台" + i);
thread.start();
}
while (flag) {
long b = System.currentTimeMillis();
if (b - a > 1 * 60 * 1000 && flag) {
//关门
flag = false;
CustomerQuene.getCustomerQuene().close();
System.out.println("关门不接客啦!");
}
System.out.println("系统运行时间:" + (b -a) + "ms");
System.out.println("系统空闲时间:" + ((b -a) * servantNum - ServantThread.getSumServeTime()));
ServantThread.print();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
猜你喜欢
- 一:前言:最近支付后台登录一段时间后如果没有任何操作,总是需要重新登录才可以继续访问页面,出现这个问题的原因就是session超时,debu
- 哈希表(HashMap)hash查询的时间复杂度是O(1)按值传递Character,Short,Integer,Long, Float,D
- 一、引言Good Good Study,Day Day Up,童鞋点个关注,不迷路,么么哒~~~MP自带的条件构造器虽然很强大,有时候也避免
- 什么是构建生命周期构建生命周期是一组阶段的序列(sequence of phases),这些构建生命周期中的每一个由构建阶段的不同列表定义,
- 前言最近有项目需要开发档案打包下载功能,其中包含很多大附件,项目使用minio存储且不在同一台服务器上,为了优化速度决定使用windows共
- 注入集合(数组、List、Map、Set)类型属性(1)创建类,定义数组,list,map,set类型属性,并且生成对应的set方法。(2)
- 一、堆的概念堆的定义:n个元素的序列{k1 , k2 , … , kn}称之为堆,当且仅当满足以下条件时:(1)ki
- 之前我们有介绍通过Spring Boot Admin来检测服务的上下线,然后进行通知功能。https://www.jb51.net/arti
- 一、返回BufferedImage由于spring mvc不支持返回BufferedImage ,所以增加图片转换器@Configurati
- SSO :同一个帐号在同一个公司不同系统上登陆 使用SpringSecurity实现类似于SSO登陆系统是十分简单的 下面我就搭
- 引导语线程池我们在工作中经常会用到。在请求量大时,使用线程池,可以充分利用机器资源,增加请求的处理速度,本章节我们就和大家一起来学习线程池。
- 本文介绍了JAVA中实现原生的 socket 通信机制原理,分享给大家,具体如下:当前环境jdk == 1.8知识点socket 的连接处理
- c#调用surfer软件,添加应用的方法:1.在项目文件上右键->添加引用2.选择COM标签页3.找到Surfer 9 type li
- 1.发生问题的场景我在用java获取一个接口的大JSON字符串,并赋值给String常量时,遇到了java: 常量字符串过长这个报错2.解决
- 对象是使用new创建的,但是并没有与之相对应的delete操作来回收对象占用的内存。当我们完成对某个对象的使用时,只需停止对该对象的引用:将
- 一:简介方法引用分为三种,方法引用通过一对双冒号:: 来表示,方法引用是一种函数式接口的另一种书写方式静态方法引用,通过类名::静态方法名,
- 本文实例为大家分享了Unity shader实现遮罩效果的具体代码,供大家参考,具体内容如下效果:shader代码:Shader "
- 本文实例讲述了Java简单验证身份证功能。分享给大家供大家参考,具体如下:package org.cxy.csdn.example;impo
- 详解Java虚拟机管理的内存运行时数据区域概述 Java虚拟机在执行Java程序的过程中会把它所管理的内
- 知乎是一个真实的网络问答社区,社区氛围友好、理性、认真,连接各行各业的精英。他们分享着彼此的专业知识、经验和见解,为中文互联网源源不断地提供