java 之 面試題-銀行業務調度
模擬實現銀行業務調度系統邏輯,具體需求如下:
銀行內有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》,并發確實是個很難說的東西,再加上時間片本來分配的問題,那幾個線程怎么可能確保同步呢。。。。
最后,看了下張老師的代碼,。。。。。居然和我的路子不一樣,我想的過于細了。結果給自己造了這么多絆子。就先這樣吧,改天再深入研究研究并發。
浙公網安備 33010602011771號