业务和需求分析:
模拟实现银行业务调度系统的逻辑,具体需求如下:
1.银行内有6个业务窗口,1-4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口
2.有三种对应类型的客户:普通客户、快速客户(办理如缴水电费、电话费之类的业务)、VIP客户
3.异步随机生成各种类型的客户,生成类型的客户的比例为:VIP客户:普通客户:快速客户 = 1:6:3
4.客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需的 时间为最小值
5.各类型的客户在其对应窗口按顺序依次办理业务
6.当VIP窗口(6号)和快速窗口(5号)没有客户等待办理业务是,可以处理普通客户的业务,而一旦有对应的客户等待办理业务时,则优先处理对应客户的业务
7.随机生成客户时间间隔以及业务办理时间最大值和最小值自定义
8.不实现GUI,只考虑系统逻辑实现,通过Log方式展现程序运行结果
面向对象的分析与设计:
* 设计时要遵循一个原则,谁拥有数据,谁就提供操作这些数据的方法
1.一个客户其实就是由银行的取号机生成的一个号码
2.由于客户有三类,每类客户的号码编制都是完全独立的,所以本程序一定要产生三个号码管理器对象,各自管理一类客户的排队号码,这三个号码管理器对象统一由一个取号机来管理,这个取号机对象在整个程序中只能有一个,所以要将其设计为单例
3.各种类型的客户在其对应窗口依次办理业务,准确的说,应该是窗口依次叫号,当一个窗口叫号时,要向对应的号码管理器取号
代码实现:
1.---------------------------------------------------------------------------------------------------
package com.itheima.bank;
public class Constants {
// 客户办理业务所需时间最小值, 单位为秒
public static int MIN_SERVICE_TIME = 1;
// 客户办理业务所需时间最大值
public static int MAX_SERVICE_TIME = 10;
// 随机生成普通客户的比例为1
public static int COMMON_CUSTOMER_INTERVAL_TIME = 1;
}
package com.itheima.bank;
public enum CustomerType {
COMMON, EXPRESS, VIP;
public String toString() {
switch (this) {
case COMMON:
return "普通";
case EXPRESS:
return "快速";
case VIP:
return "VIP";
}
return null;
}
}
3.---------------------------------------------------------------------------------------------------
package com.itheima.bank;
import java.util.ArrayList;
import java.util.List;
/**
* 号码管理器
*
* @author mrng
*/
public class NumberManager {
// 最后一个被取的号码
private int lastNumber = 1;
// 取完号码后要排队,这个集合就用来模仿队列
private List<Integer> queueNumber = new ArrayList<Integer>();
// 客户取号时调用次方法,产生新的号码
public synchronized Integer generateNewNumber() {
// 加入队列
queueNumber.add(lastNumber);
// 将号码返回给调用者
return lastNumber++;
}
// 服务窗口叫号时调用这个方法,得到在队列里面的第一个号码
public synchronized Integer fetchServiceNumber() {
if (queueNumber.isEmpty()) {
return null;
}
return queueNumber.remove(0);
}
}
package com.itheima.bank;
/**
* 取号机,只能有一个取号机存在,所以设计为单例
*
* @author mrng
*/
public class NumberMechine {
private NumberManager vipNumberManager = new NumberManager();
private NumberManager commonNumberManager = new NumberManager();
private NumberManager expressNumberManager = new NumberManager();
public NumberManager getVipNumberManager() {
return vipNumberManager;
}
public NumberManager getCommonNumberManager() {
return commonNumberManager;
}
public NumberManager getExpressNumberManager() {
return expressNumberManager;
}
private NumberMechine() {
}
private static NumberMechine instance = new NumberMechine();
// 返回取号机实例
public static NumberMechine getInstance() {
return instance;
}
}
5.---------------------------------------------------------------------------------------------------
package com.itheima.bank;
import java.util.Random;
import java.util.concurrent.Executors;
/**
* 服务窗口
*
* @author mrng
*/
public class ServiceWindow {
// 取号机
public static NumberMechine numberMechine = NumberMechine.getInstance();
// 窗口类型,和客户类型一致,默认为普通类型
private CustomerType type = CustomerType.COMMON;
// 窗口编号
private int windowId = 1;
public void setType(CustomerType type) {
this.type = type;
}
public void setWindowId(int windowId) {
this.windowId = windowId;
}
// 使窗口进入可服务状态,开始叫号并服务
public void start() {
Executors.newSingleThreadExecutor().execute(new Runnable() {
public void run() {
while (true) {
Long serviceTime = (new Random()
.nextInt(Constants.MAX_SERVICE_TIME
- Constants.MIN_SERVICE_TIME + 1) + 1) * 1000l;
switch (type) {
case COMMON:
// 叫号
service(numberMechine.getCommonNumberManager(),
serviceTime, CustomerType.COMMON);
break;
case EXPRESS:
boolean flag = service(
numberMechine.getExpressNumberManager(),
Constants.MIN_SERVICE_TIME * 1000l,
CustomerType.EXPRESS);
// 如果没有快速客户,为普通客户服务
if (!flag) {
service(numberMechine.getCommonNumberManager(),
serviceTime, CustomerType.COMMON);
}
break;
case VIP:
boolean flag2 = service(
numberMechine.getVipNumberManager(),
serviceTime, CustomerType.VIP);
// 如果没有VIP客户,为普通客户服务
if (!flag2) {
service(numberMechine.getCommonNumberManager(),
serviceTime, CustomerType.COMMON);
}
break;
}
}
}
});
}
private boolean service(NumberManager numberManager, Long serviceTime,
CustomerType customerType) {
// 叫号
Integer number = numberManager.fetchServiceNumber();
System.out.println(windowId + "号" + type + "窗口开始获取" + customerType
+ "任务");
// 如果没有叫到号,休息一秒
if (number == null) {
// 如果叫的号是普通客户并且没有叫到,说明银行现在没有排队的客户,空闲1秒
if (customerType == CustomerType.COMMON) {
System.out.println(windowId + "号" + type + "窗口没有获取到"
+ customerType + "任务,正在空闲一秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
System.out.println(windowId + "号" + type + "窗口没有获取到" + customerType
+ "任务");
return false;
} else {// 如果叫到了号,就开始为客户服务
System.out.println(windowId + "号" + type + "窗口开始为" + number + "号"
+ customerType + "客户服务");
// 开始时间
Long biginTime = System.currentTimeMillis();
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 本次服务所用的时间
Long costTime = System.currentTimeMillis() - biginTime;
System.out.println(windowId + "号" + type + "窗口为" + number + "号"
+ customerType + "客户服务完成,耗时" + costTime / 1000 + "秒");
return true;
}
}
}
6.---------------------------------------------------------------------------------------------------
package com.itheima.bank;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/***
* 主类
*
* @author mrng
*/
public class MainClass {
public static void main(String[] args) {
// 开启4个普通窗口
for (int i = 1; i <= 4; i++) {
ServiceWindow commonServiceWindow = new ServiceWindow();
commonServiceWindow.setWindowId(i);
commonServiceWindow.start();
}
// 开启1个快速窗口
ServiceWindow expressServiceWindow = new ServiceWindow();
expressServiceWindow.setType(CustomerType.EXPRESS);
expressServiceWindow.start();
// 开启1个VIP窗口
ServiceWindow vipServiceWindow = new ServiceWindow();
vipServiceWindow.setType(CustomerType.VIP);
vipServiceWindow.start();
// 制造普通客户
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
NumberMechine.getInstance().getCommonNumberManager().generateNewNumber();
}
}, 0, Constants.COMMON_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS);
// 制造快速客户
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
NumberMechine.getInstance().getExpressNumberManager().generateNewNumber();
}
}, 0, Constants.COMMON_CUSTOMER_INTERVAL_TIME * 3, TimeUnit.SECONDS);
// 制造vip客户
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
NumberMechine.getInstance().getVipNumberManager().generateNewNumber();
}
}, 0, Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6, TimeUnit.SECONDS);
}
}