模拟实现银行业务调度系统逻辑,具体需求如下:
银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。
有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP客户 :普通客户 :快速客户 = 1 :6 :3。
客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程Sleep的方式模拟)。
各类型客户在其对应窗口按顺序依次办理业务。
当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。
看了需求,就按着自己的想法开始做:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
public class Test {
DateFormat date;
// 这个是限制概率,存放的是某个类型的客户
// 内部类不允许使用静态变量,只好放这里了
static int defaultCount = 0;
static int fastCount = 0;
static int VIPCount = 0;
public Test() {
date = new SimpleDateFormat("HH:mm:ss");
}
// 客户类,代表各种客户
public class Customer {
private String name;
// 客户类型 2,1,3分表代表,快速,vip,普通客户
private int type;
private LinkedList<Customer> window;
public void setWindow(LinkedList<Customer> window) {
this.window = window;
}
public LinkedList<Customer> getWindow() {
return window;
}
// 办理业务所需时间,秒
private int time;
public int getType() {
return type;
}
public int getTime() {
return time;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Customer(int type) {
this.type = type;
Random r = new Random();
// 2是快速客户,1是vip客户,3是普通客户,这三个数是基数
// 随机一个办理业务的时间,根据客户类型算出所需时间,
// 原则的时间大小顺序是普通>VIP>快速客户
time = (this.type % 2 + this.type) * r.nextInt(4) + 5;
}
}
// 窗口类,代表了各个窗口等待容器
public class Window implements CustLinstener {
// 各个窗口容器
private LinkedList<Customer> VIP6;
private LinkedList<Customer> fast5;
// 这是个普通窗口容器
private ArrayList<LinkedList<Customer>> defaults124;
private LinkedList<Customer> default1;
private LinkedList<Customer> default2;
private LinkedList<Customer> default3;
private LinkedList<Customer> default4;
public LinkedList<Customer> getVIP6() {
return VIP6;
}
public LinkedList<Customer> getFast5() {
return fast5;
}
public LinkedList<Customer> getDefault1() {
return default1;
}
public LinkedList<Customer> getDefault2() {
return default2;
}
public LinkedList<Customer> getDefault3() {
return default3;
}
public LinkedList<Customer> getDefault4() {
return default4;
}
public Window() {
// 初始化各个窗口
default1 = new LinkedList<Customer>();
default2 = new LinkedList<Customer>();
default3 = new LinkedList<Customer>();
default4 = new LinkedList<Customer>();
defaults124 = new ArrayList<LinkedList<Customer>>();
defaults124.add(default1);
defaults124.add(default2);
defaults124.add(default3);
defaults124.add(default4);
fast5 = new LinkedList<Customer>();
VIP6 = new LinkedList<Customer>();
}
// 增加一个客户
public void addCustomer(Customer cu) {
if (cu.getType() == 2) {
cu.setWindow(fast5);
fast5.add(cu);
System.out.println(date.format(new Date()) + " --["
+ cu.getName() + "]号客户被加到了[快速窗口]队列并等待办理[快速业务]");
} else if (cu.getType() == 1) {
cu.setWindow(VIP6);
VIP6.add(cu);
System.out.println(date.format(new Date()) + " --["
+ cu.getName() + "]号客户被加到了[VIP窗口]队列并等待办理[VIP业务]");
} else {
// 普通用户的话,按原则先推到人少的窗口
int size = 0;
for (int i = 1; i < 4; i++) {
if (defaults124.get(i).size() < defaults124.get(size)
.size())
size = i;
}
defaults124.get(size).add(cu);
System.out.println(date.format(new Date()) + " --["
+ cu.getName() + "]号客户被加到了[普通窗口]队列并等待办理[普通业务]");
}
}
// 客户业务办理完毕,删除一个客户
public void removeCustomer(LinkedList<Customer> window) {
// 如果是快速窗口的业务,并且快速窗口没有人了,就从普通窗口调剂过去
if (window == fast5 && fast5.size() == 0) {
// 首先判断所有普通窗口是不是都有人
boolean usingAll = true;
for (LinkedList<Customer> w : defaults124) {
if (w.size() == 0)
usingAll = false;
}
// 都是用了的话,就调剂
if (usingAll) {
// 首先取得是哪个普通窗口人最多
int size = 0;
// 前提条件是他们都有客户
for (int i = 1; i < 5; i++) {
if (defaults124.get(i).size() > defaults124.get(size)
.size())
size = i;
}
// 将人数最多的窗口的下一个接受业务办理的客户已送到快速窗口
Customer temp = defaults124.get(size).get(1);
defaults124.get(size).remove(1);
fast5.addLast(temp);
} else {
Customer c = window.peek();
System.out.println(date.format(new Date()) + " ["
+ c.getName() + "]号客户走人");
window.poll();
}
} else {
Customer c = window.peek();
System.out.println(date.format(new Date()) + " ["
+ c.getName() + "]号客户走人");
window.poll();
}
}
@Override
public void custEvent(CustEvent e) {
// TODO Auto-generated method stub
Customer cu = new Customer(e.getType());
cu.setName(e.getName());
addCustomer(cu);
}
}
// 业务员,就那几个个窗口的业务员
public class Officer {
private Window windows;
private LinkedList<Customer> default1;
private LinkedList<Customer> default2;
private LinkedList<Customer> default3;
private LinkedList<Customer> default4;
private LinkedList<Customer> fast5;
private LinkedList<Customer> vip6;
public Officer(Window w) {
windows = w;
default1 = w.getDefault1();
default2 = w.getDefault2();
default3 = w.getDefault3();
default4 = w.getDefault4();
fast5 = w.getFast5();
vip6 = w.getVIP6();
}
// 六个业务员开始工作
public void startWork() {
// 1号业务员工作
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
if (default1.size() > 1) {
doing(default1, "普通窗口①");
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 2号业务员工作
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
if (default2.size() > 1) {
doing(default2, "普通窗口②");
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 3号业务员工作
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
if (default3.size() > 1) {
doing(default3, "普通窗口③");
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 4号业务员工作
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
if (default4.size() > 1) {
doing(default4, "普通窗口④");
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 5号快速窗口业务员工作
Thread t5 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
if (fast5.size() > 1) {
doing(fast5, "快速窗口⑤");
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 6号VIP窗口业务员工作
Thread t6 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
if (vip6.size() > 1) {
doing(vip6, "VIP窗口⑥");
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t1.setPriority(Thread.NORM_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(Thread.NORM_PRIORITY);
t4.setPriority(Thread.NORM_PRIORITY);
t5.setPriority(Thread.NORM_PRIORITY);
t6.setPriority(Thread.NORM_PRIORITY);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
private void doing(LinkedList<Customer> window, String wn)
throws InterruptedException {
Customer c = window.peek();
System.out.println(date.format(new Date()) + " [" + c.getName()
+ "]号客户在[" + wn + "]办理业务,预计时间[" + c.getTime() + "]秒");
Thread.yield();
TimeUnit.SECONDS.sleep(c.getTime());
windows.removeCustomer(window);
}
}
// 这个类随机产生客户
public class GodKing implements Runnable {
// 事件
CustAdaper ca;
// 标示客户
private Integer number;
// 随机数种子
private Random random;
// 窗口
// private Window windows;
public GodKing(Window w) {
number = 100;
random = new Random();
// windows=w;
ca = new CustAdaper();
}
// 开始生成客户
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (!Thread.interrupted()) {
// 这里来实现VIP客户 :普通客户 :快速客户 = 1 :6 :3的比例
int dtype = random.nextInt(3) + 1;
// 快速业务客户与普通客户没有达到指定比例,但vip达到了,就避免类型为vip
if (VIPCount == 1 && fastCount < 3 && defaultCount < 6) {
do {
dtype = random.nextInt(3) + 1;
} while (dtype == 1);
// 快速客户与vip客户已经是指定比例,普通的还不是
} else if (VIPCount == 1 && fastCount == 3
&& defaultCount < 6) {
do {
dtype = random.nextInt(3) + 1;
} while (dtype == 1 || dtype == 2);
} else {
// 已经是指定比例了计数器归零
VIPCount = 0;
fastCount = 0;
defaultCount = 0;
}
switch (dtype) {
case 1:
VIPCount++;
case 2:
fastCount++;
case 3:
defaultCount++;
}
// Customer c=new Customer(dtype);
// c.setName(number.toString());
// windows.addCustomer(c);
number++;
CustEvent e = new CustEvent();
e.setName(number.toString());
e.setType(dtype);
ca.notifyCustEvent(e);
// 最多10秒钟来一个人
Thread.yield();
TimeUnit.SECONDS.sleep(random.nextInt(10));
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void addCustEventListener(CustLinstener cu) {
ca.addCustListener(cu);
}
}
public static void main(String[] args) {
Test t = new Test();
Window windows = t.new Window();
Officer officers = t.new Officer(windows);
GodKing god = t.new GodKing(windows);
god.addCustEventListener(windows);
officers.startWork();
god.run();
}
// 新客户事件
public class CustEvent {
private int type;
private String name;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface CustLinstener {
public void custEvent(CustEvent e);
}
public class CustAdaper {
private Vector<CustLinstener> events = new Vector<CustLinstener>();
CustLinstener cu;
public void addCustListener(CustLinstener wc) {
events.addElement(wc);
}
public void notifyCustEvent(CustEvent e) {
Enumeration<CustLinstener> listener = events.elements();
while (listener.hasMoreElements()) {
cu = listener.nextElement();
cu.custEvent(e);
}
}
}
}
这几百行的,居然让Js着色解析时栈溢出,ie6不行啊。。。
对于一个自己学习测试用的东西,喜欢写成单类单文件,这样方便复制粘贴直接编译,内部类比较多。
写完后,发现在前5个到前6个模拟客户,业务实现逻辑总是工作不正常,整整花了半天多时间研究为神马,起初是没打算用事件的,但后来考虑到锁的问题,想想加上事件机制会不会解决,结果呢还是没有解决,开发模式却变得不伦不类,把线程优先级别改改呢,最高的模式下,居然电脑死机了。
再想想,是不是线程过多了,改成了单独两个线程,还是有点问题,最后再仔细看看《thinking in java》,并发确实是个很难说的东西,再加上时间片本来分配的问题,那几个线程怎么可能确保同步呢。。。。
最后,看了下张老师的代码,。。。。。居然和我的路子不一样,我想的过于细了。结果给自己造了这么多绊子。就先这样吧,改天再深入研究研究并发。