黑马程序员_银行业务调度系统

来源:互联网 发布:青年人休闲鞋推荐 知乎 编辑:程序博客网 时间:2024/05/20 20:42

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"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的包装类Integerpublic 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判断的是枚举类型,不可能有额外的值,为了让程序编译通过,返回nullreturn 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;//办理业务的窗口编号,默认为1private 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.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

详细请查看:<a href="http://edu.csdn.net" target="blank">http://edu.csdn.net</a>

原创粉丝点击