JAVA实现排队论

转载请注明出处: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)执行開始

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: 生成顾客线程
	 *@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 经常使用功能介绍

第三课:网络爬虫

第四课:数据库连接池

第五课:小说站点的採集

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

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

时间: 2024-11-08 10:18:58

JAVA实现排队论的相关文章

Java多线程学习(吐血超详细总结)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程

Java TM 已被阻止,因为它已过时需要更新的解决方法

公司的堡垒机需要通过浏览器登陆,且该堡垒机的网站需要Java的支持,最近通过浏览器登陆之后总是提示"java TM 已被阻止,因为它已过时需要更新的解决方法"导致登陆之后不能操作, 但是操作系统中确实已经安装了比较新的JDK,安装的JDK版本是jdk-7u67-windows-i586,因为太烦人,所以决定搞清楚报错的原因,一劳永逸,彻底解决这个问题 准备工作:安装JDK,安装版本jdk-7u67-windows-i586.exe,因为机器的Eclipse还依赖64位的JDK,所以另安

Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start(); 1 2 3 4 5 6 7 new Thread(new

由@NotNull 注解引出的关于Java空指针的控制(转)

Java 小技巧和在java应用避免NullPonintException的最佳方法 在java应用程序中,一个NullPonintException(空指针异常)是最好解决(问题)的方法.同时,空指针也是写健壮的顺畅运行的代码的关键.“预防好过治疗”这句话也同样适用于令人不爽的NullPonintException.通过应用防御性的编码技术和在遵守多个部分之间的约定,你可以再很大程度上避免NullPointException.下面的这些java小技巧可以最小化像!=null这种检查的代码.作为

Java注解(2)-注解处理器(运行时|RetentionPolicy.RUNTIME)

如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用.读取注解的工具叫作注解处理器.Java提供了两种方式来处理注解:第一种是利用运行时反射机制:另一种是使用Java提供的API来处理编译期的注解. 反射机制方式的注解处理器 仅当定义的注解的@Retention为RUNTIME时,才能够通过运行时的反射机制来处理注解.下面结合例子来说明这种方式的处理方法. Java中的反射API(如java.lang.Class.java.lang.reflect.Field等)都实现了接

jvm系列(一):java类的加载机制

java类的加载机制 原文:http://www.cnblogs.com/ityouknow/p/5603287.html 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 类加载器并不需要等到某个

Java注解(1)-注解基础

注解(Annotation)是在JAVA5中开始引入的,它为在代码中添加信息提供了一种新的方式.注解在一定程度上把元数据与源代码文件结合在一起,正如许多成熟的框架(Spring)所做的那样.那么,注解到底可以做什么呢? 1.注解的作用. 提供用来完整地描述程序所需要的信息,如编译期校验程序信息. 生成描述符文件,或生成新类的定义. 减轻编写"样板"代码(配置文件)的负担,可以使用注解自动生成. 更加干净易读的代码. 编译期类型检查. 2.Java提供的注解 Java5内置了一些原生的注

异常笔记--java编程思想

开一个新的系列,主要记一些琐碎的重要的知识点,把书读薄才是目的...特点: 代码少,概念多... 1. 基本概念 异常是在当前环境下无法获得必要的信息来解决这个问题,所以就需要从当前环境跳出,就是抛出异常.抛出异常后发生的几件事: 1.在堆上创建异常对象. 2.当前的执行路径中止                                          3. 当前环境抛出异常对象的引用.                                         4. 异常处理机制接

Java自学序言

Java自学序言 亲爱的自己和各位读者朋友: 您们好! 这是作者本人自学Java编程开发的一系列文章,不具有一定的权威性,也算是自己一个人的学习笔记和总结,希望自己通过博客的形式将我自己的学习效率得到提高.如自学的稳重存在不足或错误的地方希望广大的博客朋友们多多指教.本人在此不胜感激! 学习Java是一件很痛苦的事儿,我自己要想不断的去挑战一下自己,把自己大学所学的Java知识能够巩固起来.不断的去改正自己开发中的不足之处.如何来学习Java?如何来更好的掌握Java开发语言?这些都是我们要不断