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

本文介绍了一个模拟银行业务调度系统的项目。系统包含6个业务窗口,支持不同类型客户的服务请求,并通过多线程技术实现了随机生成及处理客户业务的逻辑。

  

----------- android培训java培训、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、随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。


二、需求分析:


<面向对象的分析与设计>

1、有三种客户:VIP客户、普通客户、快速客户。需要异步随机产生各种类型的客户。

(1)每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的。所以,要有一个号码管理器对象,让这个对象不断地产生号码,就等于随机生成了客户。

(2)由于有三类客户,每类客户的号码编排都是完全独立的,所以,本系统一共要产生三个号码管理器对象,各自管理一类用户的排队号码。这三个号码管理器对象统一由一个号码机器进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。

2、各类客户到对应的窗口办理业务,是窗口在叫号

(1)窗口内部知道如何叫号,是通过号码机器获取的号,所以它内部有这个叫号的方法

(2)各类客户并不知道怎么操作叫号的方法,只是被叫到

 

<程序的设计>


一:NumberManager类的编写 --->  产生号码的机器类

分析:

1、定义一个动态数组即集合,用于存储未服务的号码

2、定义一个产生新号码的方法generateNewManager和获取马上要为之服务的号码的方法fetchServiceNumber,这两个方法被不同的线程操作了相同的数据,所以,要进行同步。

3、同步问题:

要考虑generateNewManager和fetchServiceNumber两个方法是在两个不同的线程中,两个不同的线程要访问相同的数据lastNumber,就需要使用多线程技术

代码和注释如下: 

import java.util.ArrayList; 

import java.util.List; 

public class NumberManager { 

    //定义上一次返回的号码 

    private int lastNumber=1; 

    //定义一个集合,存放排队的队列,面向接口编程加泛型 

    private List queueNumber = new ArrayList(); 

    //两个线程访问相同的数据就会出问题 ,使用synchronized可以实现同步   

   // 将返回值类型改成Integer,使用了自动装箱和拆箱功能。 

    public synchronized Integer generateNewManager() 

    { 

        //把号码存入集合中 

        queueNumber.add(lastNumber); 

        return lastNumber++; 

    } 

    //这里要注意,取不到元素,返回的是null的时候,转成int会发生空指针异常 

    //所以我们要改成Integer,自动装箱和拆箱 

    //取服务的号码 

    public synchronized Integer fetchServiceNumber() 

    { 

        Integer number = null; 

        if(queueNumber.size()>0) 

        { 

            //取一个号码,先加进去的先取,返回值就是取走的号码 

            number = queueNumber.remove(0); 

        } 

        return number; 

    } 

} 

 

二、NumberMachine类的编写:

分析:

1、定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户的号码管理器,定义三个对应的方法来返回这三个NumberManager对象。

2、将NumberMachine类设计成单例。这三个号码管理器对象统一由一个号码机器进行管理,这个号码机器在整个系统中始终只能有一个,所以,它要被设计成单例。

代码和注释如下: 

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(); 

 

} 

三、CustomerType枚举类

 

分析:

1、系统中有三种类型的客户,所以用定义一个枚举类,其中定义三个成员分别表示三种类型的客户。

2、重写toString方法,返回类型的中文名称。这是在后面编码时重构出来的,刚开始不用考虑。

代码和注释如下:

public enum CustomerType { 

       COMMMON,EXPRESS,VIP; 

       //ServiceWindow中type的返回值应该是这个枚举的toString方法 

       public String toString() 

       { 

           switch(this) 

           { 

           case COMMMON: 

               return "普通"; 

           case EXPRESS: 

               return "快速"; 

           case VIP: 

               //枚举对象的name方法就是返回自己的名字 

               return name(); 

           } 

          return null; 

       } 

} 

四、ServiceWindow类


分析:

1.定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个不同的方法。

2.定义三个方法分别对三种客户进行服务,为了观察运行效果,应详细打印出其中的细节信息。

代码和注释如下:

import java.util.Random; 

import java.util.concurrent.Executors; 

public class ServiceWindow {  

    //这两个变量可以作为构造方法的参数 

    //定义窗口的类型 

     private CustomerType type = CustomerType.COMMMON; 

     //定义一个顺序 

     private int windowId = 1; 

     public void setType(CustomerType type) { 

            this.type = type; 

        }  

    public void setWindowId(int windowId) { 

            this.windowId = windowId; 

        }  

     //开始启动一个线程 

     public void start() 

     { 

  //jdk1.5的新的线程方法,这里只需要一个单独的线程池newSingleThreadExecutor()

和 Executors.newFixedThreadPool(1)方法是等价的 。

                //定义一个线程池,产生线程 

         Executors.newSingleThreadExecutor().execute(new Runnable() {          

            @Override 

            public void run() { 

                // TODO Auto-generated method stub 

                //取号 

                while(true) 

                { 

                    //switch的效率比if else要高 

                    switch(type){ 

                      case COMMMON: 

                          commonService(); 

                          break; 

                      case EXPRESS: 

                          expressService(); 

                          break; 

                      case VIP: 

                          vipService(); 

                          break; 

                    } 

                } 

            }         

         }); 

     } 

         private void commonService() { 

                String windowName = "第"+windowId+"号"+type+"窗口";              

                  //要号码管理器 

                  Integer number = NumberMachine.getInstance().getCommonManager().fetchServiceNumber(); 

                  //放在阻塞方法的后面,因为fetchServiceNumber()方法加了同步锁 

                  System.out.println(windowName + "正在获取服务任务"); 

                  if(number != null) 

                  { 

                      System.out.println(windowName+"为第"+number+"个"+"普通"+"客户服务"); 

                      long beginTime = System.currentTimeMillis(); 

                      int maxRand = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME; 

                       

                      long serverTime = new Random().nextInt(maxRand) + 1 + Constants.MIN_SERVICE_TIME; 

                      try { 

                        Thread.sleep(serverTime); 

                      } catch (Exception e) { 

                        // TODO Auto-generated catch block 

                        e.printStackTrace(); 

                      } 

                      long costTime   = System.currentTimeMillis()-beginTime; 

                      System.out.println(windowName+"为第"+number+"个"+"普通"+"客户完成服务,耗时"+costTime/1000+"秒"); 

                  }else{ 

                      System.out.println(windowName+"没有取得服务任务,先休息一秒钟"); 

                      try { 

                            Thread.sleep(1000); 

                          } catch (Exception e) { 

                            // TODO Auto-generated catch block 

                            e.printStackTrace(); 

                          } 

                  }    

     } 

         //也可以把express和vip窗口设计成common的子类,把其中不一样的两句代码抽象成方法 

         private void expressService() { 

                String windowName = "第"+windowId+"号"+type+"窗口";       

                  //要号码管理器 

                  Integer number = NumberMachine.getInstance().getExpressManager().fetchServiceNumber(); 

                  //放在阻塞方法的后面,因为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 serverTime = new Random().nextInt(maxRand) + 1 + Constants.MIN_SERVICE_TIME; 

                      try { 

                        Thread.sleep(Constants.MIN_SERVICE_TIME); 

                      } catch (Exception e) { 

                        // TODO Auto-generated catch block 

                        e.printStackTrace(); 

                      } 

                      long costTime   = System.currentTimeMillis()-beginTime; 

                      System.out.println(windowName+"为第"+number+"个"+type+"客户完成服务,耗时"+costTime/1000+"秒"); 

                  }else{ 

                      System.out.println(windowName+"没有取得服务任务"); 

                      commonService(); 

                  }     

         }      

         private void vipService() { 

                  String windowName = "第"+windowId+"号"+type+"窗口";               

                  //要号码管理器 

                  Integer number = NumberMachine.getInstance().getVipManager().fetchServiceNumber(); 

                  //放在阻塞方法的后面,因为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 serverTime = new Random().nextInt(maxRand) + 1 + Constants.MIN_SERVICE_TIME; 

                      try { 

                        Thread.sleep(serverTime); 

                      } catch (Exception e) { 

                        // TODO Auto-generated catch block 

                        e.printStackTrace(); 

                      } 

                      long costTime   = System.currentTimeMillis()-beginTime; 

                      System.out.println(windowName+"为第"+number+"个"+type+"客户完成服务,耗时"+costTime/1000+"秒"); 

                  }else{ 

                      System.out.println(windowName+"没有取得服务任务!"); 

                      //没有取到任务就去给普通客户服务 

                      commonService(); 

                  }                

     }     

 

五、主类(MainClass类)的编写


分析:

1、用for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。

2、再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码。

代码和注释如下:

import java.util.concurrent.Executors; 

import java.util.concurrent.TimeUnit; 

public class MainClass {  

    public static void main(String[] args) {  

              for (int i = 0; i < 5; i++) { 

             ServiceWindow commonWindow = new ServiceWindow(); 

             commonWindow.setWindowId(i); 

             commonWindow.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,  

                 Constants.COMMON_CUSTOMER_INTERVAL_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.COMMON_CUSTOMER_INTERVAL_TIME *6,  

                 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.COMMON_CUSTOMER_INTERVAL_TIME *2,  

                 TimeUnit.SECONDS 

                 ); 

    }  


六、Constants类


分析:

定义三个常量:MAX_SERVICE_TIME、MIN_SERVICE_TIME、COMMON_CUSTOMER_INTERVAL_TIME

代码和注释如下:

public class Constants {  

    public static int MAX_SERVICE_TIME=10000; 

    public static int MIN_SERVICE_TIME=1000;   

    //定义常量,客户来的间隔时间是1秒 

    public static int COMMON_CUSTOMER_INTERVAL_TIME = 1; 

} 


 

源码链接: https://pan.quark.cn/s/a4b39357ea24 斐讯K2是一款广受用户青睐的无线路由器,其运行表现稳定且具备较高的可操作性,在DIY爱好者群体中拥有极高的声誉。本资料将系统性地阐述斐讯K2的固件刷机方法及其关联的技术要点。固件升级是路由器爱好者改善设备性能、扩展功能的一种普遍手段,经由替换出厂固件,能够达成更加个性化的网络配置、增强安全防护等目标。斐讯K2固件资源库涵盖了多种知名的非官方固件,诸如Tomato Pheonix 不死鸟、高恪、PandoraBox 潘多拉等,这些固件均具备独特的优势,能够适配不同用户的需求。 1. Tomato Pheonix 不死鸟:Tomato是一款立足于Linux的开源固件,以其精巧、高效而备受推崇。不死鸟版本是专门为华硕及斐讯路由器优化的分支,提供了卓越的QoS(服务质量)配置、详尽的图表监控以及便捷的固件升级途径。对于那些需要精准调控带宽和监测网络状态的用户而言,这是一个理想的选项。 2. 高恪:高恪固件是OpenWrt的定制化版本,着重于操作的便捷性和运行的可靠性,特别适合对路由器操作不甚熟悉的用户群体。它提供了一些实用的功能,例如内置的广告屏蔽、快速测速工具等,同时保留了OpenWrt的适应性。 3. PandoraBox 潘多拉:潘多拉盒是另一款基于OpenWrt的固件,它以丰富的插件库和强大的自定义潜力而闻名。用户能够依据个人需求安装各类插件,实现更多功能,如远程接入、DDNS(动态域名解析服务)等。 4. 官方固件的纯净版本与定制版本:官方固件通常更侧重于稳定性,纯净版意味着未预置额外的应用或服务,适合注重稳定性的用户。定制版则可能包含了制造商的特色功能或优...
源码下载地址: https://pan.quark.cn/s/926926948560 AS3.0与XML结合的通用图片滚动功能,是一种基于ActionScript 3.0和XML技术的动态图像展示方案,非常适合初学者进行学习和实践应用。此项目的关键在于借助XML文件作为数据媒介,用来保存图像的相关参数,例如图像的链接地址、展示的次序等,接着在AS3.0环境中对XML进行解析,并动态地载入和展示这些图像,达成图像的滚动或是循环播放的目的。 我们需要明确ActionScript 3.0(AS3.0)是Adobe Flash Professional以及Flex Builder等开发工具中采用的编程语言,用于构建交互式内容以及丰富的互联网应用。相较于先前的版本,AS3.0在性能上有了大幅度的提升,并且引入了更为规范的面向对象编程模式,涵盖了类、接口以及包等概念。 XML(可扩展标记语言)是一种简明且高效的数据传输格式,既便于人类阅读和编写,也易于机器进行解析和生成。在该项目中,XML文件用于存储图像数据,例如图像的URL、延时的时长、动画的样式等,通过这种方式可以将数据与程序代码分离,从而增强代码的可维护性与可扩展程度。 实施这一图片滚动功能,主要涉及到以下AS3.0的核心知识点: 1. **XML解析**:运用`XML`类来载入并解析XML文件,从而获取图像的清单。AS3.0提供了简便的API来操作XML节点,例如`children()`、`attributes()`等,用以获取子节点和属性值。 2. **事件监听**:借助`EventDispatcher`类来监控载入和解析过程中的事件,比如`Event.OPEN`、`Event.PROGRESS`、`Event...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值