---------------------- <a href="http://edu.youkuaiyun.com"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.youkuaiyun.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
一、题目要求:
模拟实现银行业务调度系统逻辑,具体需求如下:
1.银行内有6个业务窗口,1- 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
2.有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
3.异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
4.客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
5.各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
6.随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
7.不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
二、项目需求分析与设计
1.需求分析
根据项目需求,加上我们现实生活中的参照,模拟一下客户去银行办理业务的流程。首先,客户来到银行,先要去取号机器那里根据所要办理的业务类型获取相应的号码,而后在大厅排队等待服务。如果银行窗口叫号到自己时,就去相应的窗口办理业务。
项目中把银行业务分为3种类型:普通业务、快速业务和VIP业务,并且每种业务类型的处理时间不同。快速业务窗口和VIP业务窗口办理完业务后可以处理普通业务。
2.设计类
从项目需求中我们可以得到:客户业务类、服务窗口类、取号机器类等,细心分析,可以发现:取号机器管理3种类型的号码,每种类型的号码都有取号和被叫号的功能。所以可以再分出一个类为号码管理器类,管理本类所产生的号码。由于银行有3种业务支持办理,我们可以把客户业务类定义成一个枚举比较方便。每种业务办理所需的时间不同,项目需求中要求可以设置时间,那么可以把业务办理的时间也定义成一个类。
三、项目编程
号码管理器类:NumberManager
具体编写步骤:
1.定义一个变量描述产生的号码的前一个号码,默认值为1。
2.定义一个集合存储取号机器打印的号码。
3.定义一个方法,用来描述取号机器不断的产生号码的功能。由于会被3种业务调用,所以要用synchronized进行同步。
4.定义银行窗口获得要服务的号码的功能,同上,也要进行同步。
package cn.isoftstone.interview.bank;
import java.util.ArrayList;
import java.util.List;
/**
* 号码管理器类
* @author 中关村阿旺
*
*/
public class NumberManager {
//产生号码的前一个号码,初始化值为1(也就是从号码1开始往后取号)
private int lastNumber=1;
//将正在取号排队等待服务的号码存放到一个集合中
//因为号码是有 顺序的,先产生哪个号码,就应该先为哪个号码服务,所以在此选择ArrayList集合。
private List<Integer> queueNumber =new ArrayList<Integer>();
//产生新号码的方法
public synchronized int generateNewNumber(){
//将产生的号码添加到集合中
queueNumber.add(lastNumber);
//让前一个号码自增就是产生的新号码
return lastNumber++;
}
//获得要服务的号码
//因为queueNumber.remove()方法返回的是一个Integer对象,如果没有客户取号,就会返回null
//所以,返回值定义成int不合适,有可能发生空指针异常,所以定义成int的包装类Integer
public synchronized Integer fetchServiceNumber(){
if(queueNumber.size()>0){
//返回排行最小的那个号码
return queueNumber.remove(0);
}
return null;
}
}
取号机器类:NumberMachine
具体编写步骤:
1.由于银行支持3种业务类型,所以要创建3个号码管理器对象,模拟现实中一个取号机可以取出不同类型的号码。
每种号码管理器对象只可以在内存中存在1个,所以私有化这3个号码管理器对象的访问。
2.定义3个公共方法,分别获取这3个号码管理器对象。
3.私有化本类的构造方法,对外提供公共的获取本类实例的方法,确保内存中只有1个取号机器类。也就是说,现实生活中的取号机只能有1个。
package cn.isoftstone.interview.bank;
/**
* 取号机器类
* @author 中关村阿旺
*
*/
public class NumberMachine {
//创建3个号码管理器对象,分别处理普通窗口、快速窗口和VIP窗口的号码
//普通窗口号码管理器
private NumberManager commonManager =new NumberManager();
//快速窗口号码管理器
private NumberManager expressManager =new NumberManager();
//VIP窗口号码管理器
private NumberManager vipManager =new NumberManager();
//创建3个方法,分别获得这3个号码管理器对象
public NumberManager getCommonManager() {
return commonManager;
}
public NumberManager getExpressManager() {
return expressManager;
}
public NumberManager getVipManager() {
return vipManager;
}
//要想让号码按照顺序取号和排队等待服务,所以每个号码管理器在内存中只能有1个对象,所以需要把本类定义成单例模式
private NumberMachine(){
}
//对外提供一个公共静态方法获取本类对象
private static NumberMachine instance =new NumberMachine();
public static NumberMachine getInstance(){
return instance;
}
}
银行业务类型类:CustomerType
具体编写步骤:
1.根据项目需求可以得出此银行支持3种业务类型,所以把此类定义成一个枚举,并创建3个业务类型对象。
2.由于测试运行时,需要给出业务类型的中文名称,所以重写枚举的toString()方法。
package cn.isoftstone.interview.bank;
/**
* 银行窗口办理的业务类型
* @author 中关村阿旺
*
*/
public enum CustomerType {
//因为银行窗口只办理3类业务:普通、快速、VIP,所以优先使用枚举来定义
COMMON,EXPRESS,VIP;
//重写toString()方法
public String toString(){
switch(this){
case COMMON:
return "普通";
case EXPRESS:
return "快速";
case VIP:
return this.name();
}
//因为switch判断的是枚举类型,不可能有额外的值,为了让程序编译通过,返回null
return null;
}
}
办理业务所耗费的时间的类:Constants
具体编写步骤:
1.自定义2个变量描述银行办理业务所要花费的最长时间和最短时间,为了让调用方便,访问修饰符设置为公共静态的。
2.定义1个变量描述默认的客户(普通客户)来银行办理业务的间隔时间,用public static修饰。
package cn.isoftstone.interview.bank;
/**
* 办理业务所需时间的类
* @author 中关村阿旺
*
*/
public class Constants {
//业务办理规定的最大时间
public static int MAX_SERVICE_TIME=10000;
//最小时间
public static int MIN_SERVICE_TIME=1000;
//普通客户来银行办理业务的间隔时间
public static int COMMON_CUSTOMER_INTERVAL_TIME=1;
}
银行窗口类:ServiceWindow
具体编写步骤:
1.定义1个银行业务枚举对象,描述银行窗口办理的业务类型,默认为普通业务。
2.定义1个变量描述银行窗口的编号。
3.定义功能设置银行窗口办理的业务类型。因为需求中说明在没有快速业务或VIP业务的情况下,快速窗口和VIP窗口可以办理普通业务。
4.定义功能设置银行窗口的编号。
5.定义功能实现银行窗口办理业务(叫号)的方法,根据每个窗口的类型判断办理何种业务。
6.实现3种业务的办理方法,办理过程使用线程的sleep()方法进行模拟,办理完毕,给出办理时间。
当快速窗口或VIP窗口的业务处理完毕,可以处理普通业务。
package cn.isoftstone.interview.bank;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 银行服务窗口类
* @author 中关村阿旺
*
*/
public class ServiceWindow {
//默认银行窗口办理业务的类型为普通业务
private CustomerType type=CustomerType.COMMON;
//办理业务的窗口编号,默认为1
private int windowId=1;
//设置窗口办理的业务类型
public void setType(CustomerType type) {
this.type = type;
}
//设置窗口的编号
public void setWindowId(int windowId) {
this.windowId = windowId;
}
//服务窗口开始叫号的方法
public void start(){
//产生一个线程池,在这个线程池中只存放一个线程
ExecutorService pool=Executors.newSingleThreadExecutor();
//使用这个线程池让服务窗口叫号
pool.execute(new Runnable() {
public void run() {
while(true){
switch(type){
case COMMON:
commonService();
break;
case EXPRESS:
expressService();
break;
case VIP:
vipService();
break;
}
}
}
});
}
//VIP窗口办理业务的方法
private void vipService() {
String windowName="第"+windowId+"号"+type+"窗口";
//调用获得办理服务的号码的方法
Integer number=NumberMachine.getInstance().getVipManager().fetchServiceNumber();
System.out.println(windowName+"正在获取VIP任务。");
if(number != null){
System.out.println(windowName+"为第"+number+"号"+type+"客户服务。");
//业务办理开始的时间
long beginTime=System.currentTimeMillis();
int maxRandomTime=Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
//业务办理所需的时间(产生的随机数位于1001~10000之间)
long serverTime=new Random().nextInt(maxRandomTime)+1+Constants.MIN_SERVICE_TIME;
try {
//让线程休眠,表示正在办理业务
Thread.sleep(serverTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//结束的时间
long endTime=System.currentTimeMillis();
System.out.println(windowName+"为第"+number+"号"+type+"客户服务完毕,耗时"+((endTime-beginTime)/1000)+"秒。");
}else{
System.out.println(windowName+"没有获取到任务。");
//没有VIP业务,那么会处理普通业务
commonService();
}
}
//快速窗口办理业务的方法
private void expressService() {
String windowName="第"+windowId+"号"+type+"窗口";
Integer number=NumberMachine.getInstance().getExpressManager().fetchServiceNumber();
System.out.println(windowName+"正在获取快速任务。");
if(number != null){
System.out.println(windowName+"为第"+number+"号"+type+"客户服务。");
//业务办理开始的时间
long beginTime=System.currentTimeMillis();
//需求中说明快速客户办理业务所需的时间为最小值
long serverTime=Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serverTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//结束的时间
long endTime=System.currentTimeMillis();
System.out.println(windowName+"为第"+number+"号"+type+"客户服务完毕,耗时"+((endTime-beginTime)/1000)+"秒。");
}else{
System.out.println(windowName+"没有获取到任务。");
//没有办理快速业务的客户,那么会处理普通客户的业务
commonService();
}
}
//普通窗口办理业务的方法
private void commonService() {
String windowName="第"+windowId+"号"+type+"窗口";
Integer number=NumberMachine.getInstance().getCommonManager().fetchServiceNumber();
System.out.println(windowName+"正在获取普通任务。");
if(number != null){
System.out.println(windowName+"为第"+number+"号普通客户服务。");
//业务办理开始的时间
long beginTime=System.currentTimeMillis();
int maxRandomTime=Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
// int nextInt(int n)
//返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。
//业务办理所需的时间(产生的随机数位于1001~10000之间)
long serverTime=new Random().nextInt(maxRandomTime)+1+Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serverTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//结束的时间
long endTime=System.currentTimeMillis();
System.out.println(windowName+"为第"+number+"号普通客户服务完毕,耗时"+((endTime-beginTime)/1000)+"秒。");
}else{
System.out.println(windowName+"没有获取到任务,空闲1秒。");
try {
//没有普通客户的业务,线程休眠1秒后再继续获取任务
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
银行业务调度系统的测试类:MainClass
具体编写步骤:
1.首先根据需求定义,创建4个普通窗口对象、1个快速窗口对象和1个VIP窗口对象,并分别设置窗口编号、类型,设置完毕后,开始办理服务。
2.使用Executors类的newScheduledThreadPool()方法创建3个线程池,每个线程池中只存放1个线程。
每隔一段时间,使此3个线程各创建1个客户对象。根据需求,客户产生的时间间隔不同。
package cn.isoftstone.interview.bank;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 银行业务调度系统测试类
*
* @author 中关村阿旺
*
*/
public class MainClass {
public static void main(String[] args) {
// 根据项目需求,创建4个普通窗口对象
for (int i = 1; i < 5; i++) {
ServiceWindow commonWindow = new ServiceWindow();
// 由于默认窗口类型是普通窗口,所以不必设置窗口类型
// 设置每个普通窗口的编号
commonWindow.setWindowId(i);
// 开始办理服务(叫号)
commonWindow.start();
}
// 创建1个快速窗口对象
ServiceWindow expressWindow = new ServiceWindow();
// 设置快速窗口的编号
expressWindow.setWindowId(5);
// 设置快速窗口的类型
expressWindow.setType(CustomerType.EXPRESS);
// 开始办理服务(叫号)
expressWindow.start();
// 创建1个VIP窗口对象
ServiceWindow vipWindow = new ServiceWindow();
// 设置VIP窗口的编号
vipWindow.setWindowId(6);
// 设置VIP窗口的类型
vipWindow.setType(CustomerType.VIP);
// 开始办理服务(叫号)
vipWindow.start();
// 创建一个线程池,线程池中有1个线程可以被调用。
ScheduledExecutorService pool1 = Executors.newScheduledThreadPool(1);
// 设置每隔多长时间调用一次指定的代码(产生普通客户)
pool1.scheduleAtFixedRate(new Runnable() {
public void run() {
//得到普通客户所取出的号码
int number=NumberMachine.getInstance().getCommonManager().generateNewNumber();
System.out.println("第"+number+"号普通客户正在等待服务!");
}
},
0, // 此方法中的代码多长时间后会被线程调用
Constants.COMMON_CUSTOMER_INTERVAL_TIME, // 以后此方法中的代码每隔多长时间会被线程调用
TimeUnit.SECONDS); // 时间单位
// 创建一个线程池,线程池中有1个线程可以被调用。
ScheduledExecutorService pool2 = Executors.newScheduledThreadPool(1);
// 设置每隔多长时间调用一次指定的代码(产生快速客户)
pool2.scheduleAtFixedRate(new Runnable() {
public void run() {
//得到快速客户所取出的号码
int number=NumberMachine.getInstance().getExpressManager().generateNewNumber();
System.out.println("第"+number+"号快速客户正在等待服务!");
}
},
0, // 此方法中的代码多长时间后会被线程调用
Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2, // 以后此方法中的代码每隔多长时间会被线程调用
TimeUnit.SECONDS); // 时间单位
// 创建一个线程池,线程池中有1个线程可以被调用。
ScheduledExecutorService pool3 = Executors.newScheduledThreadPool(1);
// 设置每隔多长时间调用一次指定的代码(产生VIP客户)
pool3.scheduleAtFixedRate(new Runnable() {
public void run() {
//得到VIP客户所取出的号码
int number=NumberMachine.getInstance().getVipManager().generateNewNumber();
System.out.println("第"+number+"号VIP客户正在等待服务!");
}
},
0, // 此方法中的代码多长时间后会被线程调用
Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6, // 以后此方法中的代码每隔多长时间会被线程调用
TimeUnit.SECONDS); // 时间单位
}
}
---------------------- <a href="http://edu.youkuaiyun.com"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.youkuaiyun.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
详细请查看:<a href="http://edu.youkuaiyun.com" target="blank">http://edu.youkuaiyun.com</a>