黑马程序员-7K面试之银行调度系统

时间:2023-02-19 18:15:19
------------- android培训java培训、java学习型技术博客、期待与您交流! ------------
         在可耻的玩了一个晚上的游戏后,花了一点时间把这个面试给搞定了。
 
      模拟实现银行业务调度系统逻辑,具体需求如下: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种客户。生成号码。 将这个号码丢到选号机里, 然后 业务窗口去获取选号机,一个号码就是一个客户。
       建议: 生成号码应该设计成方法,而不是设计成类,原因是,每个选号机都有一个生成号码的方法。 所以生成号码跟取号其实可以放在选号机里做。不同的窗口去取号,但是号码要被多个窗口去共享,所以这里生成号码的方法要做成同步的,数据是共享的。
       技术:线程池,枚举,单例。(由于取号机只有一台,谁来取号就生成一个号,所以设计成单例,用到时工作,不用就不工作了)     
       代码分析: 
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/