在可耻的玩了一个晚上的游戏后,花了一点时间把这个面试给搞定了。
模拟实现银行业务调度系统逻辑,具体需求如下:1.银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口
2.有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)
3.异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3
4.客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)
5.各类型客户在其对应窗口按顺序依次办理业务
6.当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务
7.随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置
8.不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果
分析:首先需要一个制造号码的机器, 然后选号(号码管理器), 业务的窗口, 以及3种客户。生成号码。 将这个号码丢到选号机里, 然后 业务窗口去获取选号机,一个号码就是一个客户。
建议: 生成号码应该设计成方法,而不是设计成类,原因是,每个选号机都有一个生成号码的方法。 所以生成号码跟取号其实可以放在选号机里做。不同的窗口去取号,但是号码要被多个窗口去共享,所以这里生成号码的方法要做成同步的,数据是共享的。
技术:线程池,枚举,单例。(由于取号机只有一台,谁来取号就生成一个号,所以设计成单例,用到时工作,不用就不工作了)
代码分析:
分析:首先需要一个制造号码的机器, 然后选号(号码管理器), 业务的窗口, 以及3种客户。生成号码。 将这个号码丢到选号机里, 然后 业务窗口去获取选号机,一个号码就是一个客户。
建议: 生成号码应该设计成方法,而不是设计成类,原因是,每个选号机都有一个生成号码的方法。 所以生成号码跟取号其实可以放在选号机里做。不同的窗口去取号,但是号码要被多个窗口去共享,所以这里生成号码的方法要做成同步的,数据是共享的。
技术:线程池,枚举,单例。(由于取号机只有一台,谁来取号就生成一个号,所以设计成单例,用到时工作,不用就不工作了)
代码分析:
public class NumberManager { //号码信息 private int lastNum = 1; //首先第一张票肯定是1,这不用问的 private List<Integer> queueNum = new ArrayList<Integer>(); //为了取号,需要将这个票存起来 public synchronized Integer generateNewManager(){ //用户拿票 queueNum.add(lastNum); //所以存的是上一张票 return lastNum++; //号码机取一张立马又来一张,所以是返回下一张票了 } public synchronized Integer fetchServiceNumber(){ //为用户服务 if(queueNum.size()>0){ return queueNum.remove(0);//那么,每次都是取取号机里当前的第一张票 这样说比较合理 } return null; } }
package com.work.cloudy; /** * 取号机, 取号机里有3个服务, 普通,快速,VIP * @author jython * */ public class NumberMachine { //取号机在整个系统中始终只能有一个,所以,它要被设计成单例。 //那么它提供了3种客户的服务,普通 快速 以及VIP, 由于取号必须是不能设置的,所以只给get方法 private NumberManager commonManager = new NumberManager(); private NumberManager expressManager =new NumberManager(); private NumberManager vipManager = new NumberManager(); public NumberManager getCommonManager() { return commonManager; } public NumberManager getExpressManager() { return expressManager; } public NumberManager getVipManager() { return vipManager; } private NumberMachine(){} //单例模式的3个步骤, 这里用了饿汉式, 私有化构造方法后 private static NumberMachine instance = new NumberMachine(); //内部实例化 public static NumberMachine getInstance(){ //对外提供一个静态访问方法 return instance; } }
public class ServiceWindow { private CustomerType type = CustomerType.COMMON; //窗口状态,它是一个枚举,里面有3个,普通快速以及VIP private int windowId = 1; //窗口ID,表示第几个窗口 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() { @Override public void run() { while(true){ //不停的取号 switch (type) { //外面有个线程调度定时器,判断是什么状态,接着取对应的号码 case COMMON: commonManager(); //普通客户取号,取完就开始被服务,一直到结束 break; case EXPRESS: expressManager(); //快速客户取号,取完就开始被服务,一直到结束,快速客户服务时间1秒 break; case VIP: vipManager(); //贵宾客户取号,取完就开始被服务,一直到结束 break; } } } }); } private void commonManager() { //开始取号,那么取的话我就可以知道是哪个号哪个窗口开始给普通客户的服务 String windowName = "第"+windowId+"号"+type+"窗口"; System.out.println(windowName+"号正在获取任务"); //只要取出来的有号,就开始服务了,当然如果没人取号,那就说明没有任务就不服务 Integer number = NumberMachine.getInstance().getCommonManager().fetchServiceNumber(); if(number != null){ System.out.println(windowName+"开始为第"+number+"个普通客户服务"); long beginTime = System.currentTimeMillis(); //服务时间为1~10之间 int rand = Constants.MAX_TIME-Constants.MIN_TIME; long serviceTimer = new Random().nextInt(rand)+1+Constants.MIN_TIME; try { Thread.sleep(serviceTimer); } catch (InterruptedException e) { e.printStackTrace(); } long costTime = System.currentTimeMillis()-beginTime; //根据上面模拟了服务时间,那么这里就可以打印了 System.out.println("第"+number+"个"+type+"服务Over! 消耗了"+(costTime/1000)+"秒"); }else{ //当然没有的话 就等待一秒,然后线程重新执行 System.out.println(windowName+"没有获取到任务,休息一秒嘛!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } private void expressManager() { //同普通窗口不同的是,他们取的号不一样的,这里取快速客户的号 //而且如果快速窗口空闲的话,就可以帮忙执行下普通客户 String windowName = "第"+windowId+"号"+type+"窗口"; System.out.println(windowName+"号正在获取任务"); Integer number = NumberMachine.getInstance().getExpressManager().fetchServiceNumber(); if(number != null){ System.out.println(windowName+"号开始为第"+number+"个"+type+"客户服务"); long beginTime = System.currentTimeMillis(); try { Thread.sleep(Constants.MIN_TIME); } catch (InterruptedException e) { e.printStackTrace(); } long costTime = System.currentTimeMillis()-beginTime; System.out.println("第"+number+"个"+type+"客户服务Over! 消耗了"+(costTime/1000)+"秒"); }else{ System.out.println(windowName+"没有获取到任务,休息一秒嘛!"); commonManager(); //那么快速如果没有取到不等待,会帮忙一起看下普通用户,表示很人性化 } } private void vipManager() { //同快速窗口,这里也是取贵宾自己的票号 String windowName = "第"+windowId+"号"+type+"窗口"; System.out.println(windowName+"号正在获取任务"); Integer number = NumberMachine.getInstance().getVipManager().fetchServiceNumber(); if(number != null){ System.out.println(windowName+"号开始为第"+number+"个"+type+"客户服务"); long beginTime = System.currentTimeMillis(); int rand = Constants.MAX_TIME-Constants.MIN_TIME; long serviceTimer = new Random().nextInt(rand)+1+Constants.MIN_TIME; try { Thread.sleep(serviceTimer); } catch (InterruptedException e) { e.printStackTrace(); } long costTime = System.currentTimeMillis()-beginTime; System.out.println("第"+number+"个"+type+"服务Over! 消耗了"+(costTime/1000)+"秒"); }else{ System.out.println(windowName+"没有获取到任务,休息一秒嘛!"); commonManager(); } } }
public enum CustomerType { //对应了窗口的3种功能的状态 COMMON,EXPRESS,VIP; @Override public String toString() { switch (this) { case COMMON: return "普通"; case EXPRESS: return "快速"; case VIP: return name(); } return null; } } public class Constants { public static int MAX_TIME = 10000; //窗口服务客户的最大时间 public static int MIN_TIME = 1000; //窗口服务客户的最小时间 public static int RUN_TIME = 1; //运行时间, 指的是客户进来的比例控制的时间 }
public class MianClass { public static void main(String[] args) { for (int i = 1; i < 5; i++) { //四个窗口 ServiceWindow commonWindow = new ServiceWindow(); //这里就不给窗口指定状态了.. 因为默认窗口就是普通客户 commonWindow.setWindowId(i); //分别有4个ID commonWindow.start(); //for 循环完毕 其实就有了4条线程 } ServiceWindow expressWindow = new ServiceWindow(); //快速窗口 expressWindow.setType(CustomerType.EXPRESS); //那么这里要设置成快速状态别人才知道这是快速窗口 //那么这里也可以不设置ID, 窗口ID默认是1, 而且,快速窗口只有一个 expressWindow.start(); //贵宾窗口同快速窗口 ServiceWindow vipWindow = new ServiceWindow(); vipWindow.setType(CustomerType.VIP); vipWindow.start(); //定时器, 用线程调度的方法来定时. //每隔几秒就来一个用户, 一个用户就相当于一个号, 没有取号的用户不算在内的 //那么假设这里的客户是一直来的, 也就是一直取号, //那么这里是模拟了3个窗口的用户分别在xx时间内无线取票 //vip : 普通 : 快速 == 1 : 6 : 3 //调度方法不解释,API里有 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable() { @Override public void run() { Integer number = NumberMachine.getInstance().getCommonManager().generateNewManager(); System.out.println("第" +number + "个普通客户正在等待服务"); } }, 0, Constants.RUN_TIME, TimeUnit.SECONDS ); Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable() { @Override public void run() { Integer number = NumberMachine.getInstance().getExpressManager().generateNewManager(); System.out.println("第" +number + "个快速客户正在等待服务"); } }, 0, Constants.RUN_TIME*2, TimeUnit.SECONDS ); Executors.newScheduledThreadPool(1).scheduleAtFixedRate( new Runnable() { @Override public void run() { Integer number = NumberMachine.getInstance().getVipManager().generateNewManager(); System.out.println("第" +number+ "个VIP客户正在等待服务"); } }, 0, Constants.RUN_TIME*6, TimeUnit.SECONDS ); } }
------------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
详情请查看:http://edu.csdn.net/