類比實現銀行業務調度系統邏輯,具體需求如下:
銀行內有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();}}@Overridepublic void custEvent(CustEvent e) {// TODO Auto-generated method stubCustomer 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() {@Overridepublic void run() {// TODO Auto-generated method stubtry {while (!Thread.interrupted()) {if (default1.size() > 1) {doing(default1, "普通視窗①");}}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});// 2號業務員工作Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtry {while (!Thread.interrupted()) {if (default2.size() > 1) {doing(default2, "普通視窗②");}}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});// 3號業務員工作Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtry {while (!Thread.interrupted()) {if (default3.size() > 1) {doing(default3, "普通視窗③");}}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});// 4號業務員工作Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtry {while (!Thread.interrupted()) {if (default4.size() > 1) {doing(default4, "普通視窗④");}}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});// 5號快速視窗業務員工作Thread t5 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtry {while (!Thread.interrupted()) {if (fast5.size() > 1) {doing(fast5, "快速視窗⑤");}}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});// 6號VIP視窗業務員工作Thread t6 = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubtry {while (!Thread.interrupted()) {if (vip6.size() > 1) {doing(vip6, "VIP視窗⑥");}}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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();}// 開始產生客戶@Overridepublic void run() {// TODO Auto-generated method stubtry {Thread.currentThread().setPriority(Thread.MIN_PRIORITY);while (!Thread.interrupted()) {// 這裡來實現VIP客戶 :普通客戶 :快速客戶 = 1 :6 :3的比例int dtype = random.nextInt(3) + 1;// 快速業務客戶與普通客戶沒有達到指定比例,但vip達到了,就避免類型為vipif (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 blocke.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》,並發確實是個很難說的東西,再加上時間片本來分配的問題,那幾個線程怎麼可能確保同步呢。。。。
最後,看了下張老師的代碼,。。。。。居然和我的路子不一樣,我想的過於細了。結果給自己造了這麼多絆子。就先這樣吧,改天再深入研究研究並發。