`
小驴变黑马
  • 浏览: 11220 次
文章分类
社区版块
存档分类

黑马学习日记_破解面试题之银行调度系统

阅读更多

---------------------- android开发Java培训、期待与您交流! ----------------------

 

需求分析:

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

办理银行业务的客户都要有一个号码这些号码都放在一个号码机器里面由于3中用户的号码都是相对独立的,普通客户取了1 , VIP客户同样可以取到1但是银行中的取号机可以取3种类型的号码所以取号机应该设计为单例的窗口通过叫号的方式来提醒客户来办理业务所以需要一个号码管理器来生成新的号码和告诉窗口轮到哪些号码的客户办理业务.服务窗口分为3种窗口可以为号码管理器提供的客户服务 

号码机器:

       号码机器中有3个属性,分别对应3种号码. 3种号码都是相互独立的,所以号码机器应该设置为单例的.

 

 

import java.util.*;

class NumberManager {

    privateintlastNumber=1;   //号码

    private List queueNumber= new ArrayList();//集合,用于存放号码

   

    //产生新的号码,并把号码存入集合

    public synchronizedint generateNumber (){

       queueNumber.add(lastNumber);

       returnlastNumber++;

    }

   

    //提取最前面的号码去办理业务

    public synchronizedint fetchNumber(){

       if(queueNumber.size()>0)

           returnqueueNumber.remove(0);

       else

           return 0;    

    }

}

 

 服务窗口:

服务窗口中有一个开始服务的方法,这个方法里面封装了3个线程,分别是为普通客户快速客户和VIP客户服务的窗口.每个窗口通过NumberMachine来获取要服务的号码普通窗口如果获取到了要服务的号码,就会随机产生服务用时,并且打印出来如果没有获取到号码,就休息1快速窗口和VIP窗口也要获取号码,如果没有获取到对应的号码就会去获取普通客户的号码 .为了知道获取到哪种类型的客户就要用枚举来限定3种类型枚举中的对象分别是:

COMMON , EXPRESS, VIP

import java.util.Random;

import java.util.concurrent.Executors;

 

publicclass ServiceWindow {

    private CustomerType type = CustomerType.COMMON;

    privateintwindowId = 1; // 窗口号

 

    publicvoid setType(CustomerType type) {

       this.type = type;

    }

 

    publicvoid setWindowId(int windowId) {

       this.windowId = windowId;

    }

 

    publicvoid start() {

 

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

 

           @Override

           publicvoid run() {

              // TODO Auto-generated method stub

              // 通过号码机取号

              while (true) {

                  switch (type) {

                  caseCOMMON:

                     commonService();

                     break;

                  caseEXPRESS:

                     expressService();

                     break;

                  caseVIP:

                     vipService();

                     break;

                  }

              }

           }

       });

    }

 

    //普通服务窗口

    privatevoid commonService() {

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

       System.out.println(windowName + "正在开始获取普通服务!");

       Integer serviceNumber = NumberMachine.getInstance() // 取号

              .getCommonManager().fetchNumber();

       if (serviceNumber != null) { // 如果取到号码

           System.out.println(windowName + "正在为" + serviceNumber + "号" + type

                  + "客户服务~!!");

           int costTime = new Random().nextInt(9000) + 1000;

           try {

              Thread.sleep(costTime);

           } catch (InterruptedException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

           System.out

                  .println(windowName + "为普通客户服务,一共耗时 : ( " + costTime/1000 + " ) 秒!!");

       } else {

           System.out.println(windowName + "没有获取到普通服务,休息1秒!");

           try {

              Thread.sleep(1000);

           } catch (Exception e) {

              // TODO: handle exception

              e.printStackTrace();

           }

       }

    }

   

    //快速服务窗口

    privatevoid expressService(){

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

       //取号

       System.out.println(windowName + "正在开始获取普通服务!");

       Integer serviceNumber=NumberMachine.getInstance().getExpressManager().fetchNumber();

       if(serviceNumber!=null){

           System.out.println(windowName+"正在为"+type+"客户服务");

           try {

              Thread.sleep(1000);

           } catch (Exception e) {

              // TODO: handle exception

           }

           System.out.println(windowName+"为"+type+"客户服务共用时 : ( 1 )秒!!");

       }else{

           System.out.println(windowName+"没有获取到"+type+"业务.");  //当没有获取到快速服务的时候,就去获取普通服务

           commonService();

       }

    }

 

    //VIP 服务窗口

    privatevoid vipService(){

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

       System.out.println(windowName + "正在开始获取普通服务!");

       Integer serviceName=NumberMachine.getInstance().getVIPManager().fetchNumber();

       if(serviceName!=null){

           System.out.println(windowName+"正在为"+type+"客户服务!");

           try {

              Thread.sleep(1000);

           } catch (InterruptedException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

           System.out.println(windowName+"为"+type+"客户服务共用时 : ( 1 ) 秒");

          

       }else{

           System.out.println(windowName+"没有获取到"+type+"业务.");

           commonService();

       }

    }

}

  

主函数:

要在主函数中创建 4个普通窗口 , 1个快速窗口和1VIP窗口,还要通过线程来按照一定时间创建客户.

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

 

publicclass MainClass {

    publicstaticvoid main(String[] args) {

   

       //不同客户出现的频率(时间单位为秒)

       finalint  com=1;

       finalint  express=3;

       finalint  vip=6;

      

       //创建普通服务窗口

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

           ServiceWindow common=new ServiceWindow();

           common.setType(CustomerType.COMMON);

           common.setWindowId(i);

           common.start();

       }

      

       //创建快速服务窗口

       ServiceWindow expressWin = new ServiceWindow();

       expressWin.setType(CustomerType.EXPRESS);

       expressWin.start();

      

        //创建VIP服务窗口

       ServiceWindow vipWin = new ServiceWindow();

       vipWin.setType(CustomerType.VIP);

       vipWin.start();

      

      

       //创建普通客户

       Executors.newScheduledThreadPool(1).scheduleAtFixedRate(

              new Runnable(){

 

                  @Override

                  publicvoid run() {

                     // TODO Auto-generated method stub

                     Integer serviceNumber=NumberMachine.getInstance().getCommonManager().generateNumber();

                     System.out.println("第"+serviceNumber+"普通客户等待服务!");

                  }

                 

              },

              0,

              com,

              TimeUnit.SECONDS

              );

      

       //创建快速客户

       Executors.newScheduledThreadPool(1).scheduleAtFixedRate(

              new Runnable(){

 

                  @Override

                  publicvoid run() {

                     // TODO Auto-generated method stub

                     Integer serviceNumber=NumberMachine.getInstance().getExpressManager().generateNumber();

                     System.out.println("第"+serviceNumber+"快速客户等待服务!");

                  }

                 

              },

              0,

              express,

              TimeUnit.SECONDS

              );

      

       //创建VIP客户

       Executors.newScheduledThreadPool(1).scheduleAtFixedRate(

              new Runnable(){

                 

                  @Override

                  publicvoid run() {

                     // TODO Auto-generated method stub

                     Integer serviceNumber=NumberMachine.getInstance().getVIPManager().generateNumber();

                     System.out.println("第"+serviceNumber+"VIP客户等待服务!");

                  }

              },

              0,

              vip,

              TimeUnit.SECONDS

       );

    }

}

 

  

总结:

1.关于synchronized

通过 private 关键字来保证数据对象只能被方法访问。在多线程的情况下,由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题,synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
简单的说:用Synchronized修饰的方法要访问同一个资源时,只会执行其中的一个,直到该方法访问完后释放了该资源,解除该锁,下一个方法可以访问了。
缺陷:如果声明中包含一个线程类的run(),由于线程的特点,该方法在进程的周期中会一直运行,一直保持锁定,那么该类中其他的synchronized方法就一直无法调用需要访问的资源。之后引入了sychronized块。

2.单例设计
教程中提到由于号码管理器在整个系统中始终只有一个,因此需要设计成单例。
首先什么叫单例,单例的作用和创建方法
在我们的Java应用程序中,随着应用程序的运行会创建出很多对象。但有时候我们希望在创建某类对象时,无论创建多少次该类对象只有一份在内存中。这就是单例模式。如果我们要实现单例模式首先要保证构造函数私有化,即不允许用户随意调用我本类的构造函数,因为只要通过new操作创建对象,必定要在堆中开辟空间,这样就无法实现单例了。
简单的说,就是为了保证在程序运行的过程中全局只有一个该类的实例对象;创建方法是把构造方法私有化。
进过查资料,疑惑。NumberMachine类单例是为了保证该类对象只有一个。本人是这么理解的,表示的是号码机器一次自能给一个人取号。还有的疑问是关于在numberMachine类内new3个numberManager对象。google,baidu不够人性化。

3.线程池
线程池的作用:

 线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
为什么要用线程池:
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

java中util包中有一个Execute接口。其下面的子类接口中有各种控制线程操作的方法。
1、newFixedThreadPool创建一个定长的线程池,每当提交一个任务就创建一个线程,直到达到池的最大长度。 2、newCachedThreadPool创建一个可缓存的线程池,如果当前线程池的长度超过了处理的需要时,它可以灵活地回收空闲的线程,当需求增加时,它可以灵活地增加新的线程,不会对池的长度做任何限制。 3、newSingleThreadExecutor创建一个单线程化的Executor,只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行。 4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。 5、newFixedThreadPool和newCachedThreadPool返回通用目的的ThreadPoolExecutor实例。直接使用ThreadPoolExecutor也能创建更加专有的的Executor。

 

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics