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

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

阅读更多

---------------------- 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培训、期待与您交流! ----------------------

分享到:
评论

相关推荐

    黑马程序员入学面试题

    - **进程与线程**:进程是操作系统资源分配的基本单位,而线程是CPU调度的基本单位,一个进程中可以有多个线程。 #### 20. 局部变量和成员变量区别 - **局部变量**:在方法、构造器或语句块中声明的变量,其作用域...

    黑马面试题总结

    ### 黑马面试题总结 #### 一、进程与线程状态 **知识点:** - **进程与线程的区别:** - **进程**:是系统进行资源分配和调度的基本单位,每个进程都有独立的代码和数据空间(程序上下文)。 - **线程**:是...

    黑马面试题

    "黑马面试题"这个主题很可能指的是来自知名IT教育机构“黑马程序员”所设计的一系列面试题目,这些题目旨在测试应聘者的编程基础、算法理解、系统设计能力以及对最新技术趋势的掌握。 首先,我们来探讨一些常见的...

    银行调度系统样例代码

    黑马程序员银行调度系统样例代码,共有3个主要类,2个辅助类,一个运行实例类,分别为: 1.3个主要类 NumberMachine,取号码器 NumberManager,号码管理器 ServiceWindow,业务窗口 2个辅助类 Constants,常量池 ...

    JAVAAndroid面试题讲解视频

    资源名称:JAVA Android面试题讲解视频资源目录:【】2012黑马程序员01_面试题【】2012黑马程序员02_面试题【】2012黑马程序员03_面试题【】2012黑马程序员04_面试题【】225ce844c6c817e9614d8f17ea70a640【】...

    黑马java面试题总结

    "黑马java面试题总结"很可能是一个包含了这些主题的资源包,旨在帮助Java开发者准备面试,提升对技术的理解和应用能力。 首先,Java基础知识是面试中的常考项,包括但不限于语法特性(如封装、继承、多态)、面向...

    2018年黑马程序最新面试题汇总

    "2018年黑马程序最新面试题汇总" 这个标题表明了这是一份针对2018年度编程面试的资源集合,由“黑马程序”提供,可能是一个教育机构或者在线学习平台。关键词“最新”暗示这份资料包含了当年最新的面试题目,对求职...

    《JAVA面试题》--JAVA 黑马程序员 面试宝典全学科.zip

    十余年JAVA从业经验,精通JAVA技术体系,有志于做JAVA技能提升的朋友可与我联系,交个朋友 十余年JAVA从业经验,精通JAVA技术体系,有志于做JAVA技能提升的朋友可与我联系,交个朋友 十余年JAVA从业经验,精通JAVA...

    2021Vue面试题笔记.pdf

    Vue面试题知识点总结 以下是根据提供的文件信息生成的知识点总结: 1. Vue 基础 * Vue 实例创建时,Vue 会遍历 data 中的属性,使用 Object.defineProperty(Vue 3.0 使用 proxy)将它们转换为 getter/setter,...

    2018年12月份黑马学员面试题

    【标题】2018年12月份黑马学员面试题 这份资料集合了2018年12月份期间,黑马程序员学员在求职过程中遇到的真实面试题目,涵盖了多个IT技术领域,包括但不限于Java、Python、前端开发、数据库、操作系统、网络、数据...

    Java 面试题库,2018-2019年黑马最新版Java程序员面试宝典+题库pdf.zip

    我们培训班刚发的面试题,2018年黑马最新程序员面试宝典+题库,内容包含大量java程序员初级知识点,以及框架的应用,js的基础。需要的赶紧下载准备面试吧. 2019年黑马最新版面试宝典题库 黑马 最新版面试 宝典题库

    python面试题-黑马:web+爬虫+Linux+数据库

    python面试题-黑马:web+爬虫+Linux+数据库

    黑马程序员__移动商城项目实战

    黑马程序员__移动商城项目实战 包括黑马程序员乐淘商城实战和慕课React+Antd

    黑马起爆_公式_黑马量能起爆_黑马起爆_起爆黑马_

    公式指标,黑马起爆,有助于判断什么时候可以入手

    2023黑马面试宝典-Java面试宝典大全-java面试宝典黑马

    Java面试宝典是Java程序员求职面试的重要参考资料,它涵盖了Java编程语言的核心概念、高级特性、设计模式、并发处理、框架应用、数据库交互等多个方面。以下将详细解析这些关键知识点: 1. **Java基础**:面试中,...

    2023java八股文高频面试题

    这份资源是一份2023年Java面试题集,适用于准备Java开发岗位面试的人员。本资源收集了大量的Java面试题,旨在帮助读者熟悉Java编程语言以及相关的编程技术和知识点,从而在面试中更好地展现自己的能力和潜力。本资源...

    黑马Android面试题整理

    以下是一些关于Android面试题的关键知识点,主要基于黑马程序员内部整理的资料: 1. **Activity**: - Activity是Android四大组件之一,它代表用户界面。通常,每个用户交互界面对应一个Activity。 - `...

Global site tag (gtag.js) - Google Analytics