银行业务调度系统
模拟实现银行业务调度系统逻辑,具体需求如下:
银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户:普通客户 :快速客户 = 1 :6 :3。
客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
为了使初次学习更加的清晰,我将办理的时间都规定为2秒,避免繁杂。之后将做进一步的补充。
首先,分析题意,有三种对应类型的客户:普通的客户,快速客户,VIP客户。
而对于银行来说:无论哪种用户,是通过取号来体现的,产生一个用户,也就是生产一个号,通过对号的操作,来实现对客户的操作。而号是通过取号机对应的类型,从而得到不同类型的号。对与三类客户,每类客户的号码编排是完全独立的。
对于每一个取号操作来说,都有一个号码管理器,这个号码管理器不断的产生出对应客户,也就是产生出对应的号。由于本例中有三个不同种类的客户,所以要有三个号码管理器对象,去操作各自对应类型的客户。而这三个号码管理器被一个取号机统一管理,而这个取号机在整个系统中始终有一个,所以被设计成为单例。
而对于窗口来说:其不断的叫号,其实就是去操作号码管理器,不断的从中得到当前第一个号,对其进行操作,如果没有,则等待一会继续叫号。
所以先设计取号机和号码管理器的类:如下
号码管理器类:
import java.util.*;
public class NumberManager
{
private int lastNumber = 0;//记录当前应该生成的号(也就是第几个客户来了)
private List<Integer>queueNumber =newArrayList<Integer>();
//存储已经生成的号,按照先后存入(存入等待的客户)
public synchronized Integer addNumber()
{//增加号(也就是来了一个客户)
queueNumber.add(lastNumber++);
return lastNumber;//为了显示是第几个客户来了,这里返回lastNumber。
}
public synchronizedInteger getNumber()
{//提供给柜台用于操作号码管理器中的号(也就是柜台叫号的过程)
Integer number = null;
if(queueNumber.size()!=0)
{//如果有客户,则叫当前第一个客户
number = queueNumber.remove(0)
}//如果没有客户,返回null
return number;
//柜台根据返回值,也就是有客户或者没有客户,从而进行下一步操作。如果没有,则等待一会继续叫,如果有,则操作这个客户。
}
}
取号机类
public class NumberMachine
{//单例设计模式:因为系统中只有一个实例
private NumberMachine()
{
}
public static finalNumberMachine instance = newNumberMachine();
public static NumberMachine getInstance()
{
return instance;
}
//饿汉式,避免了多线程导致的问题。
private NumberManagercommonManager =newNumberManager();
private NumberManagerexpressManager =newNumberManager();
private NumberManagervipManager =newNumberManager();
//有三个号码管理器,分别对应三种不同类型的客户。
public NumberManager getCommonManager()
{
return commonManager;
}
public void setCommonManager(NumberManager commonManager)
{
this.commonManager =commonManager;
}
public NumberManager getExpressManager()
{
return expressManager;
}
public NumberManager getVipManager()
{
return vipManager;
}
}
客户类型:由于只有三种客户,所以设计为枚举类型。
public enum CustomerType
{
COMMON,EXPRESS,VIP;
public String toString()
{
if(this==COMMON)
{
return "普通";
}
else if(this==EXPRESS)
{
return "快速";
}
else
{
return "VIP";
}
}
}
窗口类:(提供了叫号的功能,而叫号是通过号码管理器去实现的,面向对象的设计原则:谁拥有数据,谁就具有操作数据的方法)
import java.util.concurrent.Executors;
public class ServiceWindow
{
private int number = 1;//对应各自窗口的数量。
private CustomerType type = CustomerType.COMMON;
//不同的客户类型,对应不同的窗口,默认为普通窗口
public ServiceWindow()
{
}
public void setNumber(int number)
{
this.number =number;
}
public void setType(CustomerType type)
{
this.type =type;
}
//通过设置注入的方式,将窗口的类型和编号设置。
public void start()
{//每个窗口提供一个开始叫号的函数,用户窗口的启动。
Executors.newSingleThreadExecutor().execute(newRunnable(){
public void run()
{//使用线程,并使用while使各个窗口可以独立不断的去叫号,不断的处理。
while(true)
{
switch(type)
{//根据对应窗口的类型,去对应叫各自类型客户
case COMMON:
commonService();//普通客户
break;
case EXPRESS:
expressService();//快速客户
break;
case VIP:
vipService();//VIP客户
break;
}
}
}
});
}
private void commonService()
{//具体模拟叫号的过程
System.out.println("第"+number+"个"+type+"开始开始获取普通任务");
//显示哪种类型的第几个窗口开始叫号
Integer serviceNumber = NumberMachine.getInstance().getCommonManager().getNumber();
//去叫一个号,并得到这个号。
if(serviceNumber!=null)//如果有对应号存在,也就是对应类型的客户存在,则窗口处理。
{
try
{
Thread.sleep(2000); //处理时间为2s
System.out.println("......处理中。。。。");
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println("第"+number+"个"+type+"处理了第"+serviceNumber+"个普通客户任务,用了2秒");
//显示了什么类型的第几个窗口处理了第几个号
}
//如果窗口没有叫到号的话。就等待一会继续叫号。
else
{
System.out.println(number+"..."+type+"现在还没有普通客户来,等待一会吧!");
try
{
Thread.sleep(1000);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
private void expressService()
{
System.out.println("第"+number+"个"+type+"开始开始获取快速任务");
Integer serviceNumber = NumberMachine.getInstance().getExpressManager().getNumber();
if(serviceNumber!=null)
{
try
{
Thread.sleep(2000);
}
catch(Exceptione)
{
e.printStackTrace();
}
System.out.println("第"+number+"个"+"快速窗口处理了第"+serviceNumber+"个快速客户任务,用了2秒");
}
else
System.out.println("第"+number+"个快速窗口没有处理到快速业务,转去处理普通业务");
commonService();
}
}
private void vipService()
{
System.out.println("第"+number+"个"+"VIP窗口开始开始获取VIP任务");
Integer serviceNumber = NumberMachine.getInstance().getVipManager().getNumber();
if(serviceNumber!=null)
{
try
{
Thread.sleep(1000);
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println("第"+number+"个"+"VIP窗口处理了第"+serviceNumber+"个VIP客户任务,用了2秒");
}
else
{//如果没有VIP客户,则去处理普通用户
System.out.println("第"+number+"个VIP窗口没有处理到VIP业务,转去处理普通业务");
commonService();
}
}
}
主类:(产生窗口,并随机产生不同类型的客户)
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MainClass
{
public static void main(String[] args)
{
//产生4个普通窗口
for(inti=1;i<5;i++){
ServiceWindow window = new ServiceWindow();
window.setNumber(i);
window.start();
}
//产生1个快速窗口
ServiceWindow expressWindow = new ServiceWindow();
expressWindow.setType(CustomerType.EXPRESS);
expressWindow.start();
//产生1个VIP窗口
ServiceWindow vipWindow = new ServiceWindow();
vipWindow.setType(CustomerType.VIP);
vipWindow.start();
//随机产生不同的客户,按照比例
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
publicvoidrun()
{
Integer serviceNumber = NumberMachine.getInstance().getCommonManager().addNumber();
System.out.println("加入"+serviceNumber+"号普通客户");
}
},0,1,TimeUnit.SECONDS
);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
newRunnable(){
publicvoidrun()
{
Integer serviceNumber =NumberMachine.getInstance().getExpressManager().addNumber();
System.out.println("加入了一个"+serviceNumber+"号快速客户");
}
},0,3,TimeUnit.SECONDS
);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run()
{
Integer serviceNumber =NumberMachine.getInstance().getVipManager().addNumber();
System.out.println("加入"+serviceNumber+"号VIP客户");
}
},0,6,TimeUnit.SECONDS
);
}
}
它们之间的类图
附:随机生成处于1秒到10秒的处理时间。
publicclassConstants{
publicstaticintMAX_SERVICE_TIME =10000; //10秒!
publicstaticintMIN_SERVICE_TIME =1000; //1秒!
publicstaticintCOMMON_CUSTOMER_INTERVAL_TIME = 1;
}
intmaxRandom =Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;
intserviceTime =newRandom().nextInt(maxRandom)+1+Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch(InterruptedExceptione) {
e.printStackTrace();
}