黑马程序员_7K面试题—银行业务调度系统

银行排队系统模拟
本文介绍了一个银行排队系统的模拟实现,采用Java多线程技术,模拟不同类型客户的排队和办理业务过程。系统包括VIP、快速和普通窗口,实现了不同窗口间的调度和服务。

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! --------------------

一、需求

Ø 银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。

 

Ø 有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。

 

Ø 异步随机生成各种类型的客户,生成各类型用户的概率比例为:

 

        VIP客户 :普通客户 :快速客户  =  1 3

 

Ø 客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。

Ø 各类型客户在其对应窗口按顺序依次办理业务。 

Ø 当VIP6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。

Ø 随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。

Ø 不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

二、面向对象的分析与设计

有三种对应类型的客户:VIP客户,普通客户,快速客户 ,异步随机生成各种类型的客户,各类型客户在其对应窗口按顺序依次办理业务 。

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

各类型客户在其对应窗口按顺序依次办理业务 ,准确地说,应该是窗口依次叫号。创库通过号码管理器获取当前要被服务的号码。

三、类图


四、设计

NumberManager

定义一个用于存储上一个客户号码的成员变量和用于存储所有等待服务的客户号码的队列集合。

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

代码:

  import java.util.ArrayList;
  import java.util.List;
  
  //号码管理器
  public class NumberManger {
  	private int lastNumber=0;
  	private List<Integer> queNumber=new ArrayList<Integer>();
  	public synchronized Integer newMangerNumber()//生成新的号码
  	{
  		queNumber.add(++lastNumber);//把生成的号码存进集合
  		return lastNumber;
  	}
  	public synchronized Integer fetchNumber()//取号
  	{
  		if(queNumber.size()>0){
  			return (Integer)queNumber.remove(0);
  		}else{
  			return null;
  		}
  	}
  	//由于上两个方法使用了同一数据,涉及到了多线程的问题,所以要使用到synchronized
  }

NumberMachine 类

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

NumberMachine类设计成单例。

代码:

  public class NumberMachine {
	private NumberManger commonManger=new NumberManger();
	private NumberManger expressManger=new NumberManger();
	private NumberManger vipManger=new NumberManger();
	public NumberManger getCommonManger() {
		return commonManger;
	}
	public NumberManger getExpressManger() {
		return expressManger;
	}
	public NumberManger getVipManger() {
		return vipManger;
	}
	//因为只有一个机器。所以可以用单例模式
	private NumberMachine(){}
	public static NumberMachine getInstance()
	{
		return intance;
	}
	private static NumberMachine intance=new NumberMachine();
}

Customer 枚举类

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

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

代码:

public enum Customer 
{
	common,express,vip;
	public String toString()
	{
		String name = null;
		switch(this)
		{
		case common:
			name="普通";
			break;
		case express:
			name= "快速";
			break;
		case vip:
			name= name();
			break;
		}
		return name;
	}
}

ServiceWindow

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

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


import java.util.Random;
import java.util.concurrent.Executors;

public class ServiceWindow {
	private Customer type=Customer.common;
	private int WindowId=1;
	
	public void setType(Customer type) {
		this.type = type;
	}
	public Customer getType() {
		return type;
	}

	public void setWindowId(int windowId) {
		WindowId = windowId;
	}

	public void start(){
		Executors.newSingleThreadExecutor().execute(new Runnable()
		{
			public void run()
			{
				while(true)
				{
					
						switch(type)
						{
							case common:
							commonServer();
							break;
							case express:
								expressServer();
								break;
							case vip:
								vipServer();
								break;
								
								
						}
				}
			
			}

		
		});
		
	}
	private void commonServer()
	{
		String windowName="第"+WindowId+"号"+type+"窗口";
		System.out.println(windowName+"正在取票");
		 Integer num=NumberMachine.getInstance().getCommonManger().fetchNumber();
		 if(num!=null)
		 {
			 
			 int  maxRan=constants.MAX_SERVER_TIME-constants.MIN_SERVER_TIME;
			 long serverTime=new Random().nextInt(maxRan)+1+constants.MIN_SERVER_TIME;
			 try {
				Thread.sleep(serverTime);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			 System.out.println(windowName+"为"+num+"个"+type+"客户服务"+serverTime/1000+"秒");
		 }
		 else
		 {
			 System.out.println("没有取到任务,空闲一秒");
			 try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 }
	}
	private void expressServer()
	{
		String windowName="第"+WindowId+"号"+type+"窗口";
		System.out.println(windowName+"正在取票");
		 Integer num=NumberMachine.getInstance().getExpressManger().fetchNumber();
		 System.out.println(windowName + "开始获取快速任务!");
		 if(num!=null)
		 {
			 System.out.println(windowName + "开始为第" + num + "号快速客户服务");
			 int serviceTime = constants.MIN_SERVER_TIME;
			 try{
				Thread.sleep(serviceTime);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(windowName + "完成为第" + num + "号快速客户服务,总共耗时" + serviceTime/1000 + "秒");
			 
		 }
		 else
		 {
			
			 System.out.println(windowName + "没有取到快速任务!");				
			 commonServer();
		 }
	}
	private void vipServer()
	{
		String windowName="第"+WindowId+"号"+type+"窗口";
		System.out.println(windowName + "开始获取VIP任务!");
		 Integer num=NumberMachine.getInstance().getVipManger().fetchNumber();
		 if(num!=null)
		 {
			 System.out.println(windowName + "开始为第" + num + "号VIP客户服务");			
				int maxRandom = constants.MAX_SERVER_TIME - constants.MIN_SERVER_TIME;
				int serviceTime = new Random().nextInt(maxRandom)+1 + constants.MIN_SERVER_TIME;
				try {
					Thread.sleep(serviceTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}		
				System.out.println(windowName + "完成为第" + num + "号VIP客户服务,总共耗时" + serviceTime/1000 + "秒");		
			}else{
				System.out.println(windowName + "没有取到VIP任务!");				
				commonServer();
			}	
	}
}

MainClass

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class MainClass {
	
	private static Logger logger = Logger.getLogger("cn.itcast.bankqueue");
	

	public static void main(String[] args) {
		//产生4个普通窗口
		for(int i=1;i<5;i++){
			ServiceWindow window =  new ServiceWindow();
			window.setWindowId(i);
			window.start();
		}
	
		//产生1个快速窗口
		ServiceWindow expressWindow =  new ServiceWindow();
		expressWindow.setType(Customer.express);
		expressWindow.start();
		
		//产生1个VIP窗口		
		ServiceWindow vipWindow =  new ServiceWindow();
		vipWindow.setType(Customer.vip);
		vipWindow.start();		
		
		//普通客户拿号
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						Integer serviceNumber = NumberMachine.getInstance().getCommonManger().newMangerNumber();
						
						System.out.println("第" + serviceNumber + "号普通客户正在等待服务!");						
					}
				},
				0,
				constants.COMMON_CUSTOMER_INTERVAL_TIME, 
				TimeUnit.SECONDS);
		
		//快速客户拿号
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						Integer serviceNumber = NumberMachine.getInstance().getExpressManger().newMangerNumber();
						System.out.println("第" + serviceNumber + "号快速客户正在等待服务!");
					}
				},
				0,
				constants.COMMON_CUSTOMER_INTERVAL_TIME * 2, 
				TimeUnit.SECONDS);
		
		//VIP客户拿号
		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						Integer serviceNumber = NumberMachine.getInstance().getVipManger().newMangerNumber();
						System.out.println("第" + serviceNumber + "号VIP客户正在等待服务!");
					}
				},
				0,
				constants.COMMON_CUSTOMER_INTERVAL_TIME * 6, 
				TimeUnit.SECONDS);
	}

}

constants

定义三个常量: MAX_SERVER_TIME最大服务时间 ,MIN_SERVER_TIME(最快服务时间)COMMON_CUSTOMER_INTERVAL_TIME

  public class constants {

	public static int MAX_SERVER_TIME=10000;
	public static int MIN_SERVER_TIME=1000;
	/*每个普通窗口服务一个客户的平均时间为5秒,一共有4个这样的窗口,也就是说银行的所有普通窗口合起来
	 * 平均1.25秒内可以服务完一个普通客户,再加上快速窗口和VIP窗口也可以服务普通客户,所以,
	 * 1秒钟产生一个普通客户比较合理,*/	
	public static int COMMON_CUSTOMER_INTERVAL_TIME = 1; 
}

测试结果:


总结:

 

学完最后一个视频,收货还是很多。我从最初的java菜鸟到现在已经有了一定的java基础知识。这个视频也反复看了两次。看完老师讲的总感觉是这我懂,可是到最后自己去动手设计的时候总是不能有那种思想。说到底还是对面向对象这一概念理解的不是透彻。不知道应该把什么当做一个对象。不过这两个视频自己还是有动手去完成一遍,我想这样应该慢慢就会有自己的编程思想。



------------------------ ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.youkuaiyun.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值