转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/50401727

http://www.llwjy.com/blogdetail/3c3f556d2e98284111139e5690f078a1.html

个人博客站已经上线了,网址 www.llwjy.com ~欢迎各位吐槽~

-------------------------------------------------------------------------------------------------

前段时间去银行办业务,排队的人那是真多。自己正式办理业务也就不到5分钟,可是却足足等了两个小时(相信非常多人都遇到过这样的情况),对这样的服务水平真的是无语了,可是问题又来了。银行应该开几个窗体,既能保证总体的服务质量,又能保证资源资源的利用率呢?以下我们就通过排队论来模拟这个问题。

排队论简单介绍

排队论是研究系统随机聚散现象和随机系统工作project的数学理论和方法,又称随机服务系统理论。为运筹学的一个分支。

我们以下对排队论做下简化处理,先看下图:

我们在图的左側安排若干个蓝色服务台,右側为可能会过来的红色顾客,中间为黄色的等候区,假设有服务台处于空暇状态。顾客能够直接去接受服务,否则就要在黄色区域等候,顾客服务的顺序採用先到现服务的原则。如今假设我们知道顾客过来的概率分布,那么我们在左側安排几个服务台既能达到更好的服务水平,又能保证服务台的使用率?以下我们就构建模型来模拟这个问题。

排队论分步实现

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: 生成顾客线程
*@Author:lulei
*@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)执行開始

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhb2ppbWFubWFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="img">

2)顾客产生线程关闭

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhb2ppbWFubWFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="img">

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: 生成顾客线程
*@Author:lulei
*@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();
}
}
} }

-------------------------------------------------------------------------------------------------

小福利

-------------------------------------------------------------------------------------------------

      个人在极客学院上《Lucene案例开发》课程已经上线了。欢迎大家吐槽~

第一课:Lucene概述

第二课:Lucene 经常使用功能介绍

第三课:网络爬虫

第四课:数据库连接池

第五课:小说站点的採集

第六课:小说站点数据库操作

第七课:小说站点分布式爬虫的实现

JAVA实现排队论的更多相关文章

  1. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  2. 读书笔记:《数据结构与算法分析Java语言描述》

    目录 第 3 章 表.栈和队列 3.2 表 ADT 3.2.1 表的简单数组实现 3.2.2 简单链表 3.3 Java Collections API 中的表 3.3.1 Collection 接口 ...

  3. 故障重现(内存篇2),JAVA内存不足导致频繁回收和swap引起的性能问题

    背景起因: 记起以前的另一次也是关于内存的调优分享下   有个系统平时运行非常稳定运行(没经历过大并发考验),然而在一次活动后,人数并发一上来后,系统开始卡. 我按经验开始调优,在每个关键步骤的加入如 ...

  4. Elasticsearch之java的基本操作一

    摘要   接触ElasticSearch已经有一段了.在这期间,遇到很多问题,但在最后自己的不断探索下解决了这些问题.看到网上或多或少的都有一些介绍ElasticSearch相关知识的文档,但个人觉得 ...

  5. 论:开发者信仰之“天下IT是一家“(Java .NET篇)

    比尔盖茨公认的IT界领军人物,打造了辉煌一时的PC时代. 2008年,史蒂夫鲍尔默接替了盖茨的工作,成为微软公司的总裁. 2013年他与微软做了最后的道别. 2013年以后,我才真正看到了微软的变化. ...

  6. 故障重现, JAVA进程内存不够时突然挂掉模拟

    背景,服务器上的一个JAVA服务进程突然挂掉,查看产生了崩溃日志,如下: # Set larger code cache with -XX:ReservedCodeCacheSize= # This ...

  7. 死磕内存篇 --- JAVA进程和linux内存间的大小关系

    运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...

  8. 【小程序分享篇 一 】开发了个JAVA小程序, 用于清除内存卡或者U盘里的垃圾文件非常有用

    有一种场景, 手机内存卡空间被用光了,但又不知道哪个文件占用了太大,一个个文件夹去找又太麻烦,所以我开发了个小程序把手机所有文件(包括路径下所有层次子文件夹下的文件)进行一个排序,这样你就可以找出哪个 ...

  9. Java多线程基础学习(二)

    9. 线程安全/共享变量——同步 当多个线程用到同一个变量时,在修改值时存在同时修改的可能性,而此时该变量只能被赋值一次.这就会导致出现“线程安全”问题,这个被多个线程共用的变量称之为“共享变量”. ...

随机推荐

  1. django 笔记9 分页知识整理

    感谢老男孩 自定义分页 XSS:攻击 默认字符串返回 {{page_str|safe}} 前端 from django.utils.safestring import mark_safe page_s ...

  2. 网络通信-ping命令

  3. Linux 玩法

    php 跑不了,只来404 同一台linux服务器上建两个网站(www.A.com, www.B.com),现在A和B都跑起来了,但只有 A 能跑 php, B只能跑静态 html 文件,不知道哪里设 ...

  4. AngularJS 导航栏动态添加.active

    在传统jQuery中,实现导航栏动态添加.active类的思路比较简单,就是当点击的时候,清除其他.active,然后给当前类加上.active. 但是在AngularJS中,就不能再采用这种jQue ...

  5. vuejs helloworld

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. CSS 预处理语言之 less 篇

    less 前言 Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量.混合(mixin).函数等功能,让 CSS 更易维护.方便制作主题.扩充. 安装 客户端使用 // 引入 ...

  7. [译] 我最终是怎么玩转了 Vue 的作用域插槽

    原文链接:https://juejin.im/post/5c8856e6e51d456b30397f31#comment 原文地址:How I finally got my head around S ...

  8. docker操作大全

    docker 常用操作方法 查看docker版本docker version 搜索镜像docker serach 镜像名称 拉去镜像docker pull 镜像名称 查看本地镜像仓库信息docker ...

  9. python ORM理解、元类

    元类 参考链接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143191 ...

  10. ECNUOJ 2616 游黄山

    游黄山 Time Limit:1000MS Memory Limit:65536KB Total Submit:165 Accepted:52 Special Judge Description Po ...