模拟了银行窗口叫号系统的java代码。
银行业务调度系统
模拟实现银行业务调度系统逻辑,具体需求如下:
- 银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
- 有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
- 异步随机生成各种类型的客户,生成各类型用户的概率比例为: VIP客户 :普通客户 :快速客户 = 1 :6 :3。
- 客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
- 各类型客户在其对应窗口按顺序依次办理业务。
- 当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
- 随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
- 不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
代码及注释:
import java.util.*;
public class Bank {
//定义服务时间常量,后面要用。
public static final long SHORTEST_SEVICE_TIME = 1000;
public static final long LONGEST_SEVICE_TIME = 10000;
public static final long BETWEEN_SEVICE_TIME = LONGEST_SEVICE_TIME - SHORTEST_SEVICE_TIME;
public static void main (String[] args){
//定义3个队列。因为是先进先出,所以用到LinkedList效率会比较好,用Collections.synchronizedList让线程安全。
List<Integer> comm = Collections.synchronizedList (new LinkedList<Integer>());
List<Integer> exp = Collections.synchronizedList (new LinkedList<Integer>());
List<Integer> vip = Collections.synchronizedList (new LinkedList<Integer>());
//建立银行窗口
BankWindow comm1 = new BankWindow(1,WindowType.COMM);
BankWindow comm2 = new BankWindow(2,WindowType.COMM);
BankWindow comm3 = new BankWindow(3,WindowType.COMM);
BankWindow comm4 = new BankWindow(4,WindowType.COMM);
BankWindow exp5 = new BankWindow(5,WindowType.EXP);
BankWindow vip6 = new BankWindow(6,WindowType.VIP);
//银行窗口多线程开启
FuWuGuKe f1 = new FuWuGuKe(comm1,comm, exp, vip);
FuWuGuKe f2 = new FuWuGuKe(comm2,comm, exp, vip);
FuWuGuKe f3 = new FuWuGuKe(comm3,comm, exp, vip);
FuWuGuKe f4 = new FuWuGuKe(comm4,comm, exp, vip);
FuWuGuKe f5 = new FuWuGuKe(exp5,comm, exp, vip);
FuWuGuKe f6 = new FuWuGuKe(vip6,comm, exp, vip);
Thread t1 = new Thread(f1);
Thread t2 = new Thread(f2);
Thread t3 = new Thread(f3);
Thread t4 = new Thread(f4);
Thread t5 = new Thread(f5);
Thread t6 = new Thread(f6);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
//队列就不用额外的线程了,直接放在主线程里。
while (true){
DuiLie.shengChengDuiLie(comm, exp, vip);
mySleep((long)(Math.random()*900) + 200);
}
}
//定义了一个线程休眠的方式,不然要try catch好多遍。
public static void mySleep(long haomiao){
try{
Thread.sleep(haomiao);
}
catch(Exception e){
}
}
}
//服务顾客这个类准备用多线程
class FuWuGuKe implements Runnable{
private BankWindow bw;
private List<Integer> comm;
private List<Integer> exp;
private List<Integer> vip;
//创建对象的时候把bankwindow传进来,在run里使用对应对象的方法。
public FuWuGuKe(BankWindow bw, List<Integer> comm, List<Integer> exp, List<Integer> vip){
this.bw = bw;
this.comm = comm;
this.exp = exp;
this.vip = vip;
}
public void run(){
while(true){
//对象(bankwindow)先服务自己类型 的顾客
bw.service(bw.getType(), comm, exp, vip);
}
}
}
class BankWindow{
//定义窗口的id和类型,以及名字
private int id;
private WindowType type;
private String windowName;
//构造函数,设置窗口id和类型。
BankWindow(int id, WindowType type){
this.id = id;
this.type = type;
windowName = "第"+id+"号"+type +"窗口";
}
//返回对象的窗口类型的方法
public WindowType getType(){
return this.type;
}
//服务顾客的方法。因为:要处理对应类型的顾客,还有可能使用到3个类型的队列,所以全部当参数传入
public void service (WindowType type, List comm, List exp, List vip){
int customID;
long serviceTime;
System.out.println(windowName+"正在获取" + type + "客户");
if (type == WindowType.COMM){
if (comm.size() != 0){//普通类型的队列如果不为空,则处理普通顾客
//处理队列中的第一个客户,把客户的ID给customID,在打印语句中使用,下同。
customID = (int)comm.remove(0);
System.out.println(windowName+"获取到客户:"+customID+"普通客户");
//计算服务时间(单位毫秒),赋值给serviceTime,在打印语句中使用,下同。
serviceTime = Bank.SHORTEST_SEVICE_TIME + (long)(Math.random() * Bank.BETWEEN_SEVICE_TIME) ;
//调用Bank类中的mySleep方法,避免多次写try catch下同。
Bank.mySleep(serviceTime);
System.out.println(windowName+":"+customID+"普通客户服务完毕,服务时间:"+ serviceTime +"毫秒");
}
else{//没有普通顾客则休息
System.out.println(windowName+":没有待服务的普通客户,休息。");
Bank.mySleep(1000);
}
}
else if (type == WindowType.EXP){//快速类型的队列如果不为空,则处理快速顾客
if (exp.size() != 0){
customID = (int)exp.remove(0);
System.out.println(windowName+"获取到客户:"+customID+"快速客户");
serviceTime = Bank.SHORTEST_SEVICE_TIME;
//快速顾客服务时间为最短服务时间。
Bank.mySleep(serviceTime);
System.out.println(windowName+":"+customID+"快速客户服务完毕,服务时间:"+ serviceTime +"毫秒");
}
else {//没有快速类型的顾客,则处理普通顾客。有点递归的味道,不过由于处理普通顾客的方法会休息并结束,所以不是递归。下同
System.out.println(windowName+"没有获取到快速客户;尝试获取普通客户");
service(WindowType.COMM, comm, exp, vip);
}
}
else if (type == WindowType.VIP){//贵宾类型的队列如果不为空,则处理贵宾顾客
if (vip.size() != 0){
customID = (int)vip.remove(0);
System.out.println(windowName+"获取到客户:"+customID+"贵宾客户");
serviceTime = Bank.SHORTEST_SEVICE_TIME + (long)(Math.random() * Bank.BETWEEN_SEVICE_TIME) ;
Bank.mySleep(serviceTime);
System.out.println(windowName+":"+customID+"贵宾客户服务完毕,服务时间:"+ serviceTime +"毫秒");
}
else{//没有类型类型的顾客,则处理普通顾客。
System.out.println(windowName+"没有获取到贵宾客户;尝试获取普通客户");
service(WindowType.COMM, comm, exp, vip);
}
}
}
}
//生成队列的类
class DuiLie{
//定义了静态队列编号,随着类的加载而加载,
private static Integer commCust=0;
private static Integer expCust=0;
private static Integer vipCust=0;
//无法new对象,没有意义。
private DuiLie(){};
//每次产生一个随机数,根据随机数的大小,往对应的队列里加入对应编号的成员。
public static void shengChengDuiLie(List comm, List exp, List vip){
double randNum = Math.random();
//三种顾客的比例为6:3:1,所以分为用0.6和0.9把随机数分为3种类型。
if (randNum < 0.6){
commCust++;
comm.add(commCust);
System.out.println(commCust+"号普通用户抵达银行");
}
else if(randNum < 0.9){
expCust++;
exp.add(expCust);
System.out.println(expCust+"号快速用户抵达银行");
}
else{
vipCust++;
vip.add(vipCust);
System.out.println(vipCust+"号贵宾用户抵达银行");
}
}
}
//定义一个枚举,我也不是很懂。
enum WindowType{
COMM, EXP, VIP;
public String toString(){
switch (this){
case COMM:
return "普通";
case EXP:
return "快速";
case VIP:
return "贵宾";
}
return "";
}
}
有一个问题:在创建对象和线程的时候,代码大量重复,如何减少代码量?
Thread t1 = new Thread(new FuWuGuKe(new BankWindow(1,WindowType.COMM)));
Thread t2 = new Thread(new FuWuGuKe(new BankWindow(2,WindowType.COMM)));
Thread t3 = new Thread(new FuWuGuKe(new BankWindow(3,WindowType.COMM)));
Thread t4 = new Thread(new FuWuGuKe(new BankWindow(4,WindowType.COMM)));
Thread t5 = new Thread(new FuWuGuKe(new BankWindow(5,WindowType.EXP)));
Thread t6 = new Thread(new FuWuGuKe(new BankWindow(6,WindowType.VIP)));
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
这种3句变一句的应该不算。