黑马程序员___银行业务调度系统
银行业务调度系统项目需求:
模拟实现银行业务调度系统逻辑,具体需求如下:
>>银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
>>有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
>>异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
>>客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需
时间为最小值。
(提示:办理业务的过程可通过线程Sleep的方式模拟)
>>各类型客户在其对应窗口按顺序依次办理业务。
>>当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等
待办理业务的时候,则优先处理对应客户的业务。
>>随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
>>不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
面向对象的分析与设计:
客户(共有3种类型)
VIP客户、普通客户、快速客户
异步随机生成各种类型的客户,各类型客户在其对应的窗口按顺序办理业务。
“号码产生器”:产生号码,号码也可以理解为客户。
“号码管理器”:因为有3种客户,所以要有3种相对独立的号码生成器,但是取号机就一个,可以把这3个生成器放到一个号码管理器中,因为
这个机器就一个,所以要被设计成单例。
窗口依次叫号:(也就是说可以理解为:各个类型客户在其对应窗口依次办理业务)
窗口如何叫号呢?必然是问号码管理器
类名称 |
方法 |
功能简述 |
NumberMachine (银行取号码的机器) (注:号码机器管理3个号码生成器)
|
getCommonManager() |
普通客户管理器 |
getVipManager() |
VIP客户管理器 |
|
getExpressManager() |
快速客户管理器 |
|
getInstance() |
静态方法,单例 |
|
NumberManager (号码生成器) |
generateNewNumber() |
产生号码(为客户而生) |
|
fetchNumber() |
取号,即将要服务的号码 (为窗口服务) |
ServiceWindow () |
start() |
开始服务 |
commonService() |
普通服务窗口 |
|
expressService() |
快速服务窗口 |
|
vipService() |
vip服务窗口 |
(***注:参考一下,面向对象的一个重要原则:谁拥有数据,那么对这些数据进行操作的方法就交给谁。)
详细设计:(工程名BankQueue)
一:类
1,号码生成器
NumberManager.class
产号方法generateNewManager()
取号方法fetchServiceNumber()
2,号码管理器
NumberMachine.class
普通客户commonManager
快速客户expressManager
vip客户vipManager
3,服务窗口
ServiceWindow.class
类型type
顺序number
开始方法start()
普通窗口服务commonService()
快速窗口服务expressService()
vip 窗口服务 vipService()
4, 定义一个枚举类型
CustomerType.class
COMMON普通
EXPRESS快速
VIP
转换成中文toString()
5,定义常量
Constants.class
最大休息时间MAX_SERVICE_TIME
最小休息时间MIN_SERVICE_TIME
产生客户时间COMMON_CUSTOMER_INTERVAL_TIME
6,测试类
MainClass.class
4个普通窗口
1个快速窗口
1个VIP窗口
1个普通拿号线程
1个快速客户拿号
1个VIP客户拿号
二.部分所需类以及方法整理:
(1)
ExecutorService pool = Executors.newScheduledThreadPool(1). scheduleAtFixedRate(1,2,3,4)/*看下面*/;
创建一个线程池,-------------线程池(线程数)
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
//创建并执行开始时间,然后过了多少秒之后继续干,再过多少秒再干, 再过多少秒再干, 再过多少秒再干......
//(线程,过多少秒之后去做,然后过多少秒再去干,时间单位)
(2)
new Random().nextInt(10)
//返回0~9的随机数
代码实现:
------------------NumberManager.java--------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.itcast.interview.bank; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 //1,号码生成器 7 public class NumberManager { 8 9 //上一次产生的号码 10 private int lastNumber=1; 11 12 //定义一个队列 动态数组(集合) 13 //面向接口编程,所以前面用List,用List的扩展性更大. 14 private List<Integer> queueNumber = new ArrayList<Integer>(); 15 16 //①产生号码,因为有可能引发同步,就是说多个线程操作一个数据,所以要用synchronized修饰,使线程同步 17 public synchronized Integer generateNewManager() 18 { 19 //将号码存起来 20 queueNumber.add(lastNumber); 21 //返回到下一个号码 22 return lastNumber++; 23 } 24 25 //②取方法,同样有synchronized,当多个线程同时操作①②时,会产生互斥 26 //说一下返回类型Integer(可以自动装箱,拆箱),为了避免没有取到号码返回null,int话会造成空指针异常 27 public synchronized Integer fetchServiceNumber() 28 { 29 //将号码取走, 30 if(queueNumber.size()>0) 31 { 32 return (Integer)queueNumber.remove(0); 33 } 34 else 35 { 36 return null; 37 } 38 39 } 40 }
------------------NumberMachine.java---------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.itcast.interview.bank; 2 //2,号码管理器 3 public class NumberMachine { 4 //要返回3个管理器 5 private NumberManager commonManager = new NumberManager();//普通客户 6 private NumberManager expressManager = new NumberManager();//快速客户 7 private NumberManager vipManager = new NumberManager();//vip客户 8 9 public NumberManager getCommonManager() { 10 return commonManager; 11 } 12 public NumberManager getExpressManager() { 13 return expressManager; 14 } 15 public NumberManager getVipManager() { 16 return vipManager; 17 } 18 //因为就一个对象,所以要做成单例.第一步为上面的所有代码,就是说先做成一个普通对象 19 //第二步 20 //首先,构造方法私有化 21 private NumberMachine(){} 22 23 //第三步紧接着,因为不能new对象了.所以必须创建一个静态方法.静态方法返回自己类的一个对象 24 public static NumberMachine getInstance()//getInstance意思:获得实例,大家都这么叫-_-!! 25 { 26 return instance; 27 } 28 //上面返回的自己的对象所以下面必须要有变量名,而且是静态的 29 private static NumberMachine instance= new NumberMachine(); 30 31 }
-------------------ServiceWindow.java---------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.itcast.interview.bank; 2 3 import java.util.Random; 4 import java.util.concurrent.Executors; 5 import java.util.logging.Logger; 6 7 /** 8 * 没有把VIP窗口和快速窗口做成子类,是因为实际业务中的普通窗口可以随时被设置为VIP窗口和快速窗口。 9 * 10 3,服务窗口 11 */ 12 public class ServiceWindow { 13 @SuppressWarnings("unused") 14 private static Logger logger = Logger.getLogger("cn.itcast.bankqueue"); 15 //定义一个变量,来判断是哪一种类型的窗口,请看完CustomerType之后,继续回来,默认值为COMMON 16 private CustomerType type = CustomerType.COMMON; 17 //定一个顺序 18 private int number = 1; 19 20 public CustomerType getType() { 21 return type; 22 } 23 24 public void setType(CustomerType type) { 25 this.type = type; 26 } 27 28 public void setNumber(int number){ 29 this.number = number; 30 } 31 //给只能给外部一个开始的方法 32 public void start(){ 33 //new线程池,到这个线程池的内部招空闲的线程运行 34 Executors.newSingleThreadExecutor().execute( 35 new Runnable(){ 36 public void run(){ 37 //下面这种写法的运行效率低,最好是把while放在case下面 38 ////这里要实现的就是去取号 39 while(true){ 40 switch(type){ 41 case COMMON: 42 commonService(); 43 break; 44 case EXPRESS: 45 expressService(); 46 break; 47 case VIP: 48 vipService(); 49 break; 50 } 51 } 52 } 53 } 54 ); 55 } 56 57 private void commonService(){ 58 String windowName = "第" + number + "号" + type + "窗口"; 59 System.out.println(windowName + "开始获取普通任务!"); 60 Integer serviceNumber = NumberMachine.getInstance().getCommonManager().fetchServiceNumber(); 61 if(serviceNumber != null ){ 62 //定义开始时间 63 System.out.println(windowName + "开始为第" + serviceNumber + "号普通客户服务"); 64 //最大服务时间 65 int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME; 66 //服务的时间(产生一个随机时间,因为服务时间是0~8,所以+1.也就是1~9秒之间也就是1000~9000,再加上1000也就是 67 //基数为1,再加上1~9) 68 int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME; 69 //睡眠时间 70 try { 71 Thread.sleep(serviceTime); 72 } catch (InterruptedException e) { 73 e.printStackTrace(); 74 } 75 System.out.println(windowName + "完成为第" + serviceNumber + "号普通客户服务,总共耗时" + serviceTime/1000 + "秒"); 76 }else{ 77 System.out.println(windowName + "没有取到普通任务,正在空闲一秒"); 78 try { 79 Thread.sleep(1000);//没有取到任务,就休息一秒钟,然后再继续 80 } catch (InterruptedException e) { 81 e.printStackTrace(); 82 } 83 } 84 } 85 86 private void expressService(){ 87 Integer serviceNumber = NumberMachine.getInstance().getExpressManager().fetchServiceNumber(); 88 String windowName = "第" + number + "号" + type + "窗口"; 89 System.out.println(windowName + "开始获取快速任务!"); 90 if(serviceNumber !=null){ 91 System.out.println(windowName + "开始为第" + serviceNumber + "号快速客户服务"); 92 int serviceTime = Constants.MIN_SERVICE_TIME;//快读客户服务时间 93 try { 94 Thread.sleep(serviceTime);//休息时间,只能是最小值 95 } catch (InterruptedException e) { 96 e.printStackTrace(); 97 } 98 System.out.println(windowName + "完成为第" + serviceNumber + "号快速客户服务,总共耗时" + serviceTime/1000 + "秒"); 99 }else{ 100 System.out.println(windowName + "没有取到快速任务!"); 101 commonService(); 102 } 103 } 104 105 private void vipService(){ 106 107 Integer serviceNumber = NumberMachine.getInstance().getVipManager().fetchServiceNumber(); 108 String windowName = "第" + number + "号" + type + "窗口"; 109 System.out.println(windowName + "开始获取VIP任务!"); 110 if(serviceNumber !=null){ 111 System.out.println(windowName + "开始为第" + serviceNumber + "号VIP客户服务"); 112 int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME; 113 int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME; 114 try { 115 Thread.sleep(serviceTime); 116 } catch (InterruptedException e) { 117 e.printStackTrace(); 118 } 119 System.out.println(windowName + "完成为第" + serviceNumber + "号VIP客户服务,总共耗时" + serviceTime/1000 + "秒"); 120 }else{ 121 System.out.println(windowName + "没有取到VIP任务!"); 122 commonService(); 123 } 124 } 125 }
--------------------CustomerType.java---------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.itcast.interview.bank; 2 //4枚举一个类型 3 public enum CustomerType { 4 //COMMON,普通 5 //EXPRESS快速 6 //VIP 7 COMMON,EXPRESS,VIP; 8 9 //将参数转变成中文.(又恍然大悟的感觉.....) 10 public String toString() 11 { 12 String name = null; 13 switch(this) 14 { 15 case COMMON: 16 name = "普通"; 17 18 case EXPRESS: 19 name = "快速"; 20 break; 21 case VIP: 22 name = name(); 23 break; 24 } 25 return name; 26 } 27 }
----------------------Constants.java------------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.itcast.interview.bank; 2 //5,定义常量 3 public class Constants { 4 //最大休息时间 5 public static int MAX_SERVICE_TIME=10000; 6 7 //最小休息时间 8 public static int MIN_SERVICE_TIME=1000; 9 10 /*每个普通窗口服务一个客户的平均时间为5秒,一共有4个这样的窗口,也就是说银行的所有普通窗口合起来 11 * 平均1.25秒内可以服务完一个普通客户,再加上快速窗口和VIP窗口也可以服务普通客户,所以, 12 * 1秒钟产生一个普通客户比较合理,*/ 13 public static int COMMON_CUSTOMER_INTERVAL_TIME = 1; 14 15 }
---------------------MainClass.class-------------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.itcast.interview.bank; 2 3 import java.util.concurrent.Executors; 4 import java.util.concurrent.TimeUnit; 5 import java.util.logging.Logger; 6 7 //6,测试 8 public class MainClass { 9 10 @SuppressWarnings("unused") 11 private static Logger logger = Logger.getLogger("cn.itcast.interview.bank"); 12 13 14 public static void main(String[] args) { 15 //产生4个普通窗口 16 for(int i=1;i<5;i++){ 17 ServiceWindow window = new ServiceWindow(); 18 window.setNumber(i); 19 window.start(); 20 } 21 22 //产生1个快速窗口 23 ServiceWindow expressWindow = new ServiceWindow(); 24 expressWindow.setType(CustomerType.EXPRESS); 25 expressWindow.start(); 26 27 //产生1个VIP窗口 28 ServiceWindow vipWindow = new ServiceWindow(); 29 vipWindow.setType(CustomerType.VIP); 30 vipWindow.start(); 31 32 //普通客户拿号 33 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( 34 new Runnable(){ 35 public void run(){ 36 Integer serviceNumber = NumberMachine.getInstance().getCommonManager().generateNewManager(); 37 /** 38 * 采用logger方式,无法看到直观的运行效果,因为logger.log方法内部并不是直接把内容打印出出来, 39 * 而是交给内部的一个线程去处理,所以,打印出来的结果在时间顺序上看起来很混乱。 40 */ 41 //logger.info("第" + serviceNumber + "号普通客户正在等待服务!"); 42 System.out.println("第" + serviceNumber + "号普通客户正在等待服务!"); 43 } 44 }, 45 0, 46 Constants.COMMON_CUSTOMER_INTERVAL_TIME, 47 TimeUnit.SECONDS); 48 49 //快速客户拿号 50 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( 51 new Runnable(){ 52 public void run(){ 53 Integer serviceNumber = NumberMachine.getInstance().getExpressManager().generateNewManager(); 54 System.out.println("第" + serviceNumber + "号快速客户正在等待服务!"); 55 } 56 }, 57 0, 58 Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2, 59 TimeUnit.SECONDS); 60 61 //VIP客户拿号 62 Executors.newScheduledThreadPool(1).scheduleAtFixedRate( 63 new Runnable(){ 64 public void run(){ 65 Integer serviceNumber = NumberMachine.getInstance().getVipManager().generateNewManager(); 66 System.out.println("第" + serviceNumber + "号VIP客户正在等待服务!"); 67 } 68 }, 69 0, 70 Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6, 71 TimeUnit.SECONDS); 72 } 73 74 }