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