------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
Java 7K面试题之银行调度系统
1.需求:
模拟实现银行业务调度系统逻辑,具体需求如下:
银行业务调度系统
模拟实现银行业务调度系统逻辑,具体需求如下:
银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,
快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号 )窗口没有客户等待办理业务的时候,
这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
2.示意图
3.源代码:
第一种方法:
NumberManager类(号码管理器)
定义一个用于存储上一个客户号码的成员变量和用于存储所有等待服务的客户号码的队列集合。
定义一个产生新号码的方法和获取马上要为之服务的号码的方法,这两个方法被不同的线程操作了相同的数据,所以,要进行同步
import java.util.ArrayList;
import java.util.List;
/*号码管理器负责号码的生成和获取(返回)*/
public class NumberManager {
private int number = 1;// 每个号码管理器生成号码都从1开始
private List<Integer> storeNumbers = new ArrayList<Integer>();
public synchronized int generateNumber() {// 由客户去取生成的号码
storeNumbers.add(number);
return number++;
}
public synchronized Integer fetchNumber() {// 由服务窗口获取号码
if (storeNumbers.size() != 0)
return storeNumbers.remove(0);// 返回集合中第一个元素,队头元素保证FIFO
else
return null;// 集合中可能没有元素
} /* 窗口的取动作与客户的放动作在操作同一个集合,使用同步,这里的锁为它们对应的 同一个号码管理器实例 */
}
package 地方;
/*NumberMachine类(取号机器)
定义三个成员变量分别指向三个NumberManager对象,分别表示普通、
快速和VIP客户的号码管理器,定义三个对应的方法来返回这三个NumberManager对象。
将NumberMachine类设计成单例。*/
/*
由于该类只有一个实例,因此成员commonManager的值不变
也就是说多次调用getCommonManager()获取到的是同一个 负责统一管理三个号码管理器,
NumberMachine在程序运行期间只需要一个实例设计成单例. */
public class NumberMachine {
private NumberManager commonManager = new NumberManager();
private NumberManager vipManager = new NumberManager();
private NumberManager expressManager = new NumberManager();
public NumberManager getCommonManager() {
return commonManager;
}
public NumberManager getVIPManager() {
return vipManager;
}
public NumberManager getExpressManager() {
return expressManager;
} /* NumberMachine在程序运行时在内存限制只有一个实例 */
private NumberMachine() {
}
private static final NumberMachine ref = new NumberMachine();
public static NumberMachine getInstance() {
return ref;
}
}
/*两个枚举:windowType和TimeConstant
系统中有三种类型的客户,所以用定义一个枚举类,其中定义三个成员分别表示三种类型的客户。
重写toString方法,一方面出于考虑国人习惯,另一方面是因为switch语句比if?else语句效率高。*/
public enum WindowType {
COMMON, VIP, EXPRESS;
@Override
public String toString() {
switch (this) {
case COMMON:
return "普通";
case VIP:
return "VIP";
case EXPRESS:
return "快速";
default:
return null;
}
}
}
/*定义三个常量:
MAX_SERVICE_TIM、MIN_SERVICE_TIME、COMMON_CUSTOMER_INTERVAL_TIME
增加可阅读性。//设置客户办理业务所需时间的最大值和最小值*/
public class TimeConstant {
public static final int MIN_SERVICE_TIME = 2;// 服务最小时间
public static final int MAX_SERVICE_TIME = 10;// 服务最大时间
public static final int COMMON_CUSTOMER_INTERVAL_TIME = 1;// 每个普通用户来的时间间隔1秒
public static final int VIP_CUSTOMER_INTERVAL_TIME = 6;// 每个VIP用户来的时间间隔6秒
public static final int EXPRESS_CUSTOMER_INTERVAL_TIME = 2;// 每个快速用户来的时间间隔2秒}
}
/*ServiceWindow类
定义一个start方法,内部启动一个线程,根据服务窗口的类别分别循环调用三个不同的方法。
定义三个方法分别对三种客户进行服务,为了观察运行效果,应详细打印出其中的细节信息。*/
import java.util.Random;
import java.util.concurrent.Executors;
public class ServiceWindow {
private WindowType windowType = WindowType.COMMON;// 把三种窗口类型定义成枚举
private int windowID = 1;// 窗口的编号
public void setType(WindowType windowType) {
// 通过set方法设置窗口类型和窗口编号进行设置
this.windowType = windowType;
// 而没有通过构造方法在创建对象时传入
}
// 这样做可以修改已创建服务窗口对象的ID和type
public void setWindowID(int windowID) {
this.windowID = windowID;
}
public void start() {
// 相当于启动了NumberMachine,根据服务的不同
// 获取不同号码管理器,然后在获取号码
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
while (true) {// 不断的获取服务窗口不断获取服务(fetchNumber)
switch (windowType) {
case COMMON:
commonService();
break;
case VIP:
vipService();
break;
case EXPRESS:
expressService();
}
}
}
});
}
// 普通客户服务方法
public void commonService() {
String windowName = windowID + "号" + windowType + "窗口";
Integer serviceNumber = NumberMachine.getInstance().getCommonManager()
.fetchNumber();
// 服务窗口开始拿号,为该号客户提供服务
System.out.println(windowName + "正在获取任务");
if (serviceNumber != null) {// 当号码不为空时
System.out.println(windowName + "正在为第" + serviceNumber + "个"
+ "普通客户服务");
int minServiceTime = TimeConstant.MIN_SERVICE_TIME;
int maxServiceTime = TimeConstant.MAX_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxServiceTime
- minServiceTime)
+ 1 + minServiceTime;// 随机生成2~10秒
try {// 服务时间
Thread.sleep(serviceTime * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "为第" + serviceNumber + "个"
+ "普通客户服务了" + serviceTime + "s");
// 这里没用windowType而固定使用"普通"字符串这是因为例如:在vipService方法中没
// 有vip服务时,会调用commonService此时this.windowType=VIP,会错误打印VIP
// 客户服务
} else {
System.out.println(windowName + "窗口没有获取到" + "普通任务休息1秒钟再去获取");
try {
Thread.sleep(1000);// 服务窗口休息1秒后再去拿号
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public void vipService() {
String windowName = windowID + "号" + windowType + "窗口";
Integer serviceNumber = NumberMachine.getInstance().getVIPManager()
.fetchNumber();
// 服务窗口开始拿号,为该号客户提供服务
System.out.println(windowName + "正在获取任务");
if (serviceNumber != null) {// 取到number,开始服务
System.out.println(windowName + "正在为第" + serviceNumber + "个"
+ windowType + "客户服务");
int minServiceTime = TimeConstant.MIN_SERVICE_TIME;
int maxServiceTime = TimeConstant.MAX_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxServiceTime
- minServiceTime)
+ 1 + minServiceTime;
// 随机生成2~10秒
try {
Thread.sleep(serviceTime * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "为第" + serviceNumber + "个"
+ windowType + "客户服务了" + serviceTime + "s");
} else {
System.out.println(windowName + "窗口没有获取到" + windowType + "任务");
commonService();// VIP窗口没有获取到VIP任务,获取普通任务去服务,直接调用了commonService(),去普通任务集合去取
}
}
// 快速客户的方法
public void expressService() {
String windowName = windowID + "号" + windowType + "窗口";
Integer serviceNumber = NumberMachine.getInstance().getExpressManager()
.fetchNumber();
// 服务窗口开始拿号,为该号客户提供服务
System.out.println(windowName + "正在获取任务");
if (serviceNumber != null) {
// 取到number,开始服务
System.out.println(windowName + "正在为第" + serviceNumber + "个"
+ windowType + "客户服务");
int serviceTime = TimeConstant.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "为第" + serviceNumber + "个"
+ windowType + "客户服务了" + serviceTime + "s");
} else {
System.out.println(windowName + "窗口没有获取到" + windowType + "任务");
commonService();
// express窗口没有获取到express任务,获取普通任务去服务,直接调用了 commonService()
}
}
}
MainClass类
用for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。
接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/*MainClass类
用for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。
接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码*/
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
package 地方;
/*MainClass类
用for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。
接着再创建三个定时器,分别定时去创建新的普通客户号码、新的快速客户号码、新的VIP客户号码*/
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MainClass {
public static void main(String[] args) {
/* 创建六个窗口对象(,1个VIP窗口,1个快速窗口) */
// 4个普通窗口
for (int i = 1; i < 5; ++i) {
ServiceWindow commonWindow = new ServiceWindow();
commonWindow.setWindowID(i);
commonWindow.start();
// 开启服务
}
// 1个VIP窗口
/*
* 模拟客户来取 VIP客户 :普通客户 :快速客户 = 1 :6 :3
* 这个比例通过计时器来完成,假设每隔1秒来一个普通客户,每隔2秒来来一个快速客户. 每隔六秒来一个VIP客户.
* 假设程序运行了12秒,来了12(12/1)个普通客户, 6个快速客户(12/2),2个VIP客户(12/6) 2:12:6=1:6:3;
*/
ServiceWindow vipWindow = new ServiceWindow();
vipWindow.setType(WindowType.VIP);
vipWindow.setWindowID(6);
vipWindow.start();
// 1个快速窗口
ServiceWindow expressWindow = new ServiceWindow();
expressWindow.setType(WindowType.EXPRESS);
expressWindow.setWindowID(5);
expressWindow.start();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
int number = NumberMachine.getInstance().getCommonManager()
.generateNumber();
System.out.println("第" + number + "号普通客户等待服务");
}
}, 1, TimeConstant.COMMON_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
int number = NumberMachine.getInstance().getVIPManager()
.generateNumber();
System.out.println("第" + number + "号VIP客户等待服务");
}
}, 1, TimeConstant.VIP_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
int number = NumberMachine.getInstance().getExpressManager()
.generateNumber();
System.out.println("第" + number + "号快速客户等待服务");
// 显示vip客户
}
}, 1, TimeConstant.EXPRESS_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS);
// 设置时间单位秒
}
}
第二种方法:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Test {
// protected Logger log = Logger.getLogger(this.getClass());
// 号码生成封装Map
private Map<CustomerType, AtomicInteger> nm = new HashMap<CustomerType, AtomicInteger>();
{
nm.put(CustomerType.COMMON, new AtomicInteger(1));
nm.put(CustomerType.EXPRESS, new AtomicInteger(1));
nm.put(CustomerType.VIP, new AtomicInteger(1));
}
// 排号队列封装Map
private Map<CustomerType, BlockingQueue<Integer>> qm = new HashMap<CustomerType, BlockingQueue<Integer>>();
{
qm.put(CustomerType.COMMON, new LinkedBlockingQueue<Integer>());
qm.put(CustomerType.EXPRESS, new LinkedBlockingQueue<Integer>());
qm.put(CustomerType.VIP, new LinkedBlockingQueue<Integer>());
}
// 呼叫应答模拟队列
private BlockingQueue<Semaphore> crq = new LinkedBlockingQueue<Semaphore>();
enum CustomerType {
COMMON, EXPRESS, VIP;
public String toString() {
switch (this) {
case COMMON:
return "【普通】";
case EXPRESS:
return "【快速】";
}
return "【VIP】"; }
}
/**
* 服务窗口
*/
class Service extends Thread {
private CustomerType[] types;
private String name;
// 信号量媒介--一次只能服务一人
private Semaphore sem = new Semaphore(1);
public Service(CustomerType[] types, String name) {
this.types = types;
this.name = name;
}
public void run() {
int length = types.length;
int i = 0;<span style="font-family: Arial, Helvetica, sans-serif;">//如果要持续工作套入 while(true) i=0;重置代码</span>
while (i < length) {
CustomerType type = types[i];
BlockingQueue<Integer> q = qm.get(type);
try {
Integer num = q.poll(100, TimeUnit.MILLISECONDS);
if (num != null) {
// 呼叫signal-call
sem.acquire();
System.out.println(this.name + "正在呼叫~" + num + "~号" + type.toString() + "!");
crq.put(sem);
// 等待服务wait
sem.acquire();
// 成功继续
System.out.println(this.name + "开始为~" + num + "~号" + type.toString() + "服务!");
// 服务中 --多步操作完全可以用线程模拟
Thread.sleep(5000);
// 服务完毕
System.out.println(this.name + "为~" + num + "~号" + type.toString() + "服务完毕!");
sem.release();
//数组次序确定优先级
i = 0;
} else {
i++;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 生成客户
*/
class GenerateCustomer extends Thread {
private CustomerType type;
public GenerateCustomer(CustomerType type) {
this.type = type;
}
public void run() {
while (true) {
try {
AtomicInteger num = nm.get(type);
int i = num.getAndIncrement();
BlockingQueue<Integer> q = qm.get(type);
System.out.println(i + "~号" + type.toString() + "领号等待!");
q.put(i);
// 每隔3s生成一个客户
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//
}
}
}
/**
* 模拟应答-无记名
*/
class CallRes extends Thread {
public void run() {
while (true) {
try {
Semaphore sem = crq.take();
// 响应一下,释放许可
sem.release();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Test test=new Test();
ExecutorService threadPool = Executors.newFixedThreadPool(18);
// 创建服务窗口
// 1-4
threadPool.execute(test.new Service(new CustomerType[]{CustomerType.COMMON},"【窗口1】"));
threadPool.execute(test.new Service(new CustomerType[]{CustomerType.COMMON},"【窗口2】"));
threadPool.execute(test.new Service(new CustomerType[]{CustomerType.COMMON},"【窗口3】"));
threadPool.execute(test.new Service(new CustomerType[]{CustomerType.COMMON},"【窗口4】"));
// 5
threadPool.execute(test.new Service(new CustomerType[]{CustomerType.EXPRESS,CustomerType.COMMON},"【窗口5】"));
// 6
threadPool.execute(test.new Service(new CustomerType[]{CustomerType.VIP,CustomerType.COMMON},"【窗口6】"));
// 生成客户1:6:3
threadPool.execute(test.new GenerateCustomer(CustomerType.VIP));
for(int i=0;i<6;i++)
threadPool.execute(test.new GenerateCustomer(CustomerType.COMMON));
for(int i=0;i<3;i++)
threadPool.execute(test.new GenerateCustomer(CustomerType.EXPRESS)); //启动模拟应答
threadPool.execute(test.new CallRes());
}
}
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------