引入:

前段时间去银行办业务,排队的人那是真多,自己正式办理业务也就不到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 customers = new LinkedList();

//下一个顾客过来最短时间

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 customers = new LinkedList();

//下一个顾客过来最短时间

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();

}

}

}

}

以上就是关于Java实现排队论的原理详细介绍,希望对大家的学习有所帮助。

java 排队实现_Java实现排队论的原理相关推荐

  1. java排队论代码_Java实现排队论的原理

    摘要:这篇Java开发技术栏目下的"Java实现排队论的原理",介绍的技术点是"Java.排队论.原理.实现",希望对大家开发技术学习和问题解决有帮助. 引入: ...

  2. java final 实例_Java中final实现原理的深入分析(附示例)

    本篇文章给大家带来的内容是关于Java中final实现原理的深入分析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. final在Java中是一个保留的关键字,可以声明成员变 ...

  3. java 字节缓冲_Java字节缓冲流原理与用法详解

    本文实例讲述了Java字节缓冲流原理与用法.分享给大家供大家参考,具体如下: 一 介绍 BufferInputStresm和BufferOutputStream 这两个流类为IO提供了带缓冲区的操作, ...

  4. java switch 类型_Java switch case数据类型原理解析

    这篇文章主要介绍了Java switch case数据类型原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Java 中 switch cas ...

  5. java 内省机制_Java反射及 IoC原理、内省机制

    JAVA反射及IoC原理.JAVA内省 1. 反射反射是框架设计的灵魂,使用前提:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码文件). 1.1 反射概述主要指程序可 ...

  6. java substring实现_Java中substring()工作原理

    01.substring() 是干嘛的 sub 是 subtract 的缩写,因此 substring 的字面意思就是"把字符串做个减法".这样一分析,是不是感觉方法的命名还是蛮有 ...

  7. java排队叫号_java多线程(4)模拟排队叫号程序,不能出现交替执行的结果

    package com.javaconcurrencyprogramming.chapter1; /** * @description: 模拟有错误的排队叫号程序 * @author: * @crea ...

  8. java 缓存一致性_Java多线程——CPU缓存原理和缓存一致性问题

    说起Java中的多线程,就不得不说volatile关键词volatile关键词执行修饰变量和实例变量,不能修饰方法参数,局部变量和实例常量. volatile是Java提供的一种轻量级的同步机制,在并 ...

  9. java 分布式事务_Java核心知识 Spring原理十五 JPA 原理

    1. 事务 事务是计算机应用中不可或缺的组件模型,它保证了用户操作的原子性 ( Atomicity ).一致性 ( Consistency ).隔离性 ( Isolation ) 和持久性 ( Dur ...

  10. java lambda 实现_Java 8 Lambda实现原理分析

    PDF文档已上传Github 为了支持函数式编程,Java 8引入了Lambda表达式,那么在Java 8中到底是如何实现Lambda表达式的呢? Lambda表达式经过编译之后,到底会生成什么东西呢 ...

最新文章

  1. python去哪里学-学Python从哪里开始?
  2. Async Solr Queries in Python
  3. python有大括号吗_只有我一个人觉得Python取消了大括号而显得结构更加混乱了吗?...
  4. [RabbitMQ]常用命令
  5. 往java里输入坐标值_java.让用户输入x坐标,和y坐标。当用户输入完x坐标(比如200),敲enter,...
  6. 笔记一 Redis基础
  7. Netty入门教程——初识Netty
  8. ORACLE查看某个表空间里有哪些表
  9. linux 卡在grub_关于linux开机进入grub问题的解决方法
  10. 与程序员朋友闲聊 通用权限管理系统有啥用?
  11. 【数字信号处理】基于matlab数字信号软阈值+硬阈值+改进阈值小波去噪【含Matlab源码 068期】
  12. 云端抢红包能从服务器看到我微信操作吗,微信抢红包暗藏规律 悄悄告诉你诀窍...
  13. C#使用Thread.Sleep()导致程序无响应的解决办法
  14. MAC电脑突然开不了机的解决方案
  15. Visio 导出图片时字符间距错乱
  16. 优矿 pandas plt 显示平安银行基金月最大召回率
  17. 游戏本地文件乱码问题
  18. Android熄屏与亮屏控制
  19. DSCTF2022 fuzzerinstrospector-Wp
  20. 产品三大文档BRD/MRD/PRD

热门文章

  1. 陶瓷天线和PCB天线以及IPEX天线三者间区别
  2. 九型人格,工作中的好帮手.
  3. Web常见前端面试题及答案
  4. 在线MAC地址查询和在线随机生成MAC地址
  5. 目标跟踪之MOSSE算法(C++版本配置及原理简介)
  6. 【管理】日报,周报,会议记录模板
  7. 三步教会你在solidworks中画铝型材
  8. 接收邮件的服务器称为,接收邮件服务器
  9. ps用计算机,教你用photoshop绘制计算器
  10. 蓝桥杯真题:k倍区间