--------- android培训java培训、期待与您交流!----------



此银行业务调度系统项目习题由传智播客学员提供,经由张孝祥老师解答。

注:

   1、本博文中对该项目的理解属个人拙见,有什么错误希望大神们能发表言论。

   2、为了方便理解,一些步骤的演变过程没有删除。可能需要花点时间仔细阅读。

   3、文章后附,银行业务调度系统课件其源码,希望对大家有所帮助。


/*
银行业务调度系统
需求:
模拟实现银行业务调度系统逻辑,具体需求如下:
1、银行内有6个业务窗口,1-4号窗口为普通窗口,5号窗口为快速
窗口,6号窗口为VIP窗口。
2、有三种对应类型的窗户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
3、异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户:普通客户:快速客户 = 1 : 6 : 3。
4、客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
5、各类型客户在其对应窗口按顺序依次办理业务。
6、当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而
一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
7、随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
8、不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
*/


一、面向对象分析:

以图形的方式来说明:

103431632.png

二、代码体现

1、定义一个号码管理器,用来生成号码(客户)。

package com.itheima.bank;
import java.util.ArrayList;
import java.util.List;
public class NumberManager {
    private int lastNumber = 1;
    /*
    取号器的产生的数字一般比处理业务时的号码要大,所以处理业务的
    号码应该是正在排除的号码,而不是取号器产生的最后一个号码,怎么
    按排除的顺序取呢?那么这时就需要再定义一个动态数组,也就是集合。
    */
    private List<Integer> queueNumber = new ArrayList<Integer>();
    //产生新的管理器
//  public synchronized int generateNewManager(){
        public synchronized Integer generateNewManager(){
        //每产生一个号码(客户),就把它添加到集合。
        queueNumber.add(lastNumber);
        return lastNumber++;
    }
    /*因为当remove()方法没有取到元素时会返回null,这时候就是在
    null和int之间转换,会发生空指针异常。*/
    //业务窗口要来取号,所以学要定义一个取号的方法。
//  public int fetchServiceNumer(){
    public Integer fetchServiceNumber(){
                                                                                                                                   
        Integer number = null;
        if(queueNumber.size() > 0){
            //取的时候也按顺序取,0就为队列的第一个。
            number = queueNumber.remove(0);
        }
        return number;
                                                                                                                                   
        //remove返回值就是被取出的那个值,思考为什么要remove
        //return queueNumber.remove(0);
    }
}


2、创建一个号码器,用来产生不同客户的类型,也就是号码管理器的类型,有三种。定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户的号码管理器,定义三个对应的方法来返回这三个NumberManager对象。

package com.itheima.bank;
public class NumberMachine {
    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(){}
    public static NumberMachine getInstance(){
        return instance;
    }
    private static NumberMachine instance = new NumberMachine();
                                                                                              
/*  懒汉式,不适合多线程,且效率不高,
 *  private static NumberMachine instance;
    private NumberMachine(){}
    public static NumberMachine getInstance(){
        if (instance == null) {
            instance = new NumberMachine();  
        }
        return instance;
    }
    解决并发访问的方法:
    private static NumberMachine instance;
    private NumberMachine(){}
    public static NumberMachine getInstance(){
        if (instance == null) {
            synchronized(NumberMachine.class){
                if (instance == null) {
                    instance = new NumberMachine();
                }
            }
        }
        return instance;
    }*/
                                                                                              
                                                                                              
}


3、接下来就定义业务窗口类了,用来处理业务,其间用到枚举,和一些常量,所以另外还要定义两个类。

package com.itheima.bank;
import java.util.Random;
import java.util.concurrent.Executors;
public class ServiceWindow {
                                                                         
    /*因为有不同的窗口,所以还要定义一个int变量,又不知道窗口是哪种类型的且只有
    3种,所以用枚举(用数字表示不严谨,因为数字可以参与运算),这就需要再定义一个枚举类。
    private int type = 0;*/
    private CustomerType type = CustomerType.COMMON;
    private int windowId = 1;
    public void setType(CustomerType type) {
        this.type = type;
    }
    public void setWindowId(int windowId) {
        this.windowId = windowId;
    }
    /* 当客户把要求提交后,那么做什么事就是窗口的事了,也就是说每一个客户
     * 提交请求后,窗口就是开一个线程来处理它,newSingleThreadExecutor是
     * 指该线程池中只有一个线程在执行,提交请求后,线程池就是把它提交给
     * 空闲的线程。*/
    public void start(){
        Executors.newSingleThreadExecutor().execute(new Runnable(){
            @Override
            public void run() {
                //取号
                while (true) {
                    switch (type) {
                    case COMMON:
                        commonService();
                        break;
                    case EXPRESS:
                        expressService();
                        break;
                    case VIP:
                        vipService();
                        break;
                    }
                }
            }    
        });
    }
                                                                         
    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 + "个" + type + "客户服务");
            long beginTime = System.currentTimeMillis();
            /*int maxRand = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
            long serveTime = new Random().nextInt(maxRand) + 1 + Constants.MIN_SERVICE_TIME;
            我觉得源代码这里完全可以改成下面的代码,使用源代码,反而不好理解。*/
            long serveTime = new Random().nextInt(Constants.MAX_SERVICE_TIME) + Constants.MIN_SERVICE_TIME;
            try {
                Thread.sleep(serveTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long costTime = System.currentTimeMillis() - beginTime;
            System.out.println(windowName + "为第" + number + "个" + type + "客户完成服务,耗时" + costTime/1000 + "秒");
        }else {
            System.out.println(windowName + " 没有取到任务,先休息1秒");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
                                                                                 
        }
    }
    //与上面的不同的地方:
//  1.Integer number = NumberMachine.getInstance().getExpressManager().fetchServiceNumber();
//  2. 快速客户的处理业务为最小值。
    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();
            try {
                //需求中:要求快速用户的处理业务时长最短。
                Thread.sleep(Constants.MIN_SERVICE_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long costTime = System.currentTimeMillis() - beginTime;
            System.out.println(windowName + "为第" + number + "个" + type + "客户完成服务,耗时" + costTime/1000 + "秒");
        }else {
            /*快速客户,express当它没有取到任务的时候,不会休息,它会调用commonService,也就是会
             为普通客户服务*/
            System.out.println(windowName + " 没有取到任务!");
            commonService();
        }
    }
                                                                         
    private void vipService() {
        String windowName = "第" + windowId + "号" + type + "窗口";
        Integer number = NumberMachine.getInstance().getVipManager().fetchServiceNumber();
        System.out.println(windowName + "正在获取任务");
        if (number != null) {
            System.out.println(windowName + "为第" + number + "个" + type + "客户服务");
            long beginTime = System.currentTimeMillis();
            /*int maxRand = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
            long serveTime = new Random().nextInt(maxRand) + 1 + Constants.MIN_SERVICE_TIME;
            我觉得源代码这里完全可以改成下面的代码,使用源代码,反而不好理解。*/
            long serveTime = new Random().nextInt(Constants.MAX_SERVICE_TIME) + Constants.MIN_SERVICE_TIME;
            try {
                Thread.sleep(serveTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long costTime = System.currentTimeMillis() - beginTime;
            System.out.println(windowName + "为第" + number + "个" + type + "客户完成服务,耗时" + costTime/1000 + "秒");
        }else {
            /*VIP当它没有取到任务的时候,它不会休息,它会调用commonService,也就是会
             为普通客户服务*/
            System.out.println(windowName + " 没有取到任务!");
            commonService();
        }
    }
}


4、定义一个枚举类,客户的类型。

package com.itheima.bank;
//因为客户类型是固定的,所以可以定义一个枚举类。
public enum CustomerType {
    //分别为普通、快速、VIP
    COMMON,EXPRESS,VIP;
    //覆盖toString方法,打印输出的格式
    public String toString(){
        switch(this){
        case COMMON:
            return "普通";
        case EXPRESS:
            return "快速";
        case VIP:
            return name();
        }
        return null;
    }
}


5、再定义一个常量的类型,专门管理一些常量,比如时间等。

注意类型的名称constants,以后写代码也可以这么定义。

package com.itheima.bank;
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;
}


6、最后定义一个测试类。

package com.itheima.bank;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MainClass {
    public static void main(String[] args) {
        for(int i = 1; i < 5; i++){
            ServiceWindow  conmmonWindow = new ServiceWindow();
            conmmonWindow.setWindowId(i);
            conmmonWindow.start();
        }
                
        ServiceWindow  expressWindow = new ServiceWindow();
        expressWindow.setType(CustomerType.EXPRESS);
        expressWindow.start();
                
        ServiceWindow  vipWindow = new ServiceWindow();
        vipWindow.setType(CustomerType.VIP);
        vipWindow.start();
                
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
                new Runnable(){
                    @Override
                    public void run() {
                        Integer number = NumberMachine.getInstance().getCommonManager().generateNewManager();
                        System.out.println(number + "号普通客户等待服务...");
                    }
                },
                0,
                //每6秒产生6个
                Constants.COMMON_CUSTOMER_INTERVAL_TIME,
                TimeUnit.SECONDS
            );
                
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
                new Runnable(){
                    @Override
                    public void run() {
                        Integer number = NumberMachine.getInstance().getVipManager().generateNewManager();
                        System.out.println(number + "号VIP客户等待服务...");
                    }
                },
                0,
                //每6秒产生1个
                Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6,
                TimeUnit.SECONDS
            );
                
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
                new Runnable(){
                    @Override
                    public void run() {
                        Integer number = NumberMachine.getInstance().getExpressManager().generateNewManager();
                        System.out.println(number + "号快速客户等待服务...");
                    }
                },
                0,
                //每6秒产生3个
                Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2,
                TimeUnit.SECONDS
            );
    }
}



--------- android培训java培训、期待与您交流!----------