銀行業務調度系統
---------------------- ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! ----------------------
銀行業務調度系統邏輯,需求如下:
1、 六個業務窗口。1—4號窗口爲普通窗口,5號窗口爲快速窗口,6號窗口爲VIP窗口。
2、 三種對應類型客戶:VIP客戶,普通客戶,快速客戶(辦理如交水電費,電話費之類業務)。
3、 異步隨機生成各種類型的客戶。生成各類用戶概率比例VIP:普通:快速=1:6:3。
4、 客戶辦理業務所需時間有最大值和最小值。在該範圍內隨機設定每個VIP客戶和普通客戶辦理業務所需的時間,快速客戶辦理業務所需時間爲最小值(提示:辦理業務過程可通過線程Sleep方式模擬)。
5、 各類型客戶在其對應窗口按順序依次辦理業務。
6、 當VIP(6號)窗口和快速業務(5號)窗口沒有客戶等待辦理業務的時候,這兩個窗口可以處理普通客戶的業務,而一旦有對應客戶等待辦理業務時,則優先處理對應客戶的業務。
7、 隨機生成客戶時間間隔以及業務辦理時間最大值和最小值自定,可以設置。
面向對象分析設計
1、三種客戶類型,隨機生成,各類客戶在其對應窗口按順序依次辦理業務。
號碼管理器對象,這個對象不斷產生號碼,等於隨機生成客戶。由於有三類客戶,每類客戶號碼編排都是完全獨立的,所以本系統要產生三個號碼管理器。這三個號碼管理器有一個號碼機器進行管理,這個號碼機器在整個系統中始終只能有一個,所以它要被設計成單例模式。
2、各個類型客戶在其對應窗口按順序依次辦理業務,也就是窗口依次叫號。
服務窗口每次找號碼管理器獲取當前要被服務的號碼。
NumberManager和NumberMachine類
(1)NumberManager類
定義一個用於存儲上一個客戶號碼的成員變量和用於存儲所有等待服務的客戶號碼的隊列集合。
定義一個產生新號碼的方法和獲取馬上要爲之服務的號碼的方法,這兩個方法被不同的線程操作了相同的數據,所以,要進行同步。
(2)NumberMachine類
定義三個成員變量分別指向三個NumberManager對象,分別表示普通、快速和VIP客戶的號碼管理器,定義三個對應的方法來返回這三個NumberManager對象。
將NumberMachine類設計成單例。
ServiceWindow與CustomerType枚舉類
(1)CustomerType枚舉類
系統中有三種類型的客戶,所以用定義一個枚舉類,其中定義三個成員分別表示三種類型的客戶。
重寫toString方法,返回類型的中文名稱。這是在後面編碼時重構出來的,剛開始不用考慮。
(2)ServiceWindow類
定義一個start方法,內部啓動一個線程,根據服務窗口的類別分別循環調用三個不同的方法。
定義三個方法分別對三種客戶進行服務,爲了觀察運行效果,應詳細打印出其中的細節信息。
MainClass類與Constants類
(1)MainClass類
用for循環創建出4個普通窗口,再創建出1個快速窗口和一個VIP窗口。
接着再創建三個定時器,分別定時去創建新的普通客戶號碼、新的快速客戶號碼、新的VIP客戶號碼。
(2)Constants類
定義三個常量:MAX_SERVICE_TIME、MIN_SERVICE_TIME、COMMON_CUSTOMER_INTERVAL_TIME
import java.util.*;
//號碼管理器類。不斷產生號碼,隨機生成客戶。
public class NumberManager {
private int lastNumber=1;//上一次返回的號碼
private List<Integer> queueNumber=new ArrayList<Integer>();//存儲排隊的號碼集合。List利於面向接口編程
public synchronized Integer generateNewManager(){//生成新客戶
queueNumber.add(lastNumber);//產生一個號碼就存起來
return lastNumber++;
}
public synchronized Integer fetchServiceNumber(){//取要服務的號
Integer number=null;
if(queueNumber.size()>0){
number=queueNumber.remove(0);//把第一個取走。返回值就是取得的號
}
return number;
}
//兩個不同的線程調用這個對象身上的兩個方法,客戶來了調generateNewManager方法,窗口服務取下一個號用fetchServiceNumber方法。
//兩個不同的線程訪問相同的數據就會出問題。用synchronized關鍵字實現兩個線程間互斥,這樣多個線程操作就會形成互斥。
}
//管理3個號碼管理器。單例模式
public class NumberMachine {
private NumberManager commonManager=new NumberManager();
private NumberManager expressManager=new NumberManager();
private NumberManager vipManager=new NumberManager();
public NumberManager getCommonManager() {
return commonManager;
}
public NumberManager getExpressManager() {
return expressManager;
}
public NumberManager getVipManager() {
return vipManager;
}
private NumberMachine(){}//構造方法私有化,單例設計模式
public static NumberMachine getInstance(){//靜態方法返回自己的對象
return instance;
}
private static NumberMachine instance=new NumberMachine();
}
import java.util.Random;
import java.util.concurrent.Executors;
public class ServiceWindow {
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){//不停的取號
switch(type){//switch比if else效率高
case COMMON:
commonService();
break;
case EXPRESS:
expressService();
break;
case VIP:
vipService();
break;
}
}
}
private void commonService() {
String windowName="第"+windowId+"號"+type+"窗口";
Integer num=NumberMachine.getInstance().getCommonManager().fetchServiceNumber();//通過NumberMachine獲得普通號碼管理器,取號
System.out.println(windowName+"正在獲取任務");
if(num!=null){
System.out.println(windowName+"爲第"+num+"個"+"普通"+"客戶服務!");//VIP窗口爲普通客戶服務,窗口是VIP,但客戶是普通客戶,所以不能用type
long beginTime=System.currentTimeMillis();
int maxRand=Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;//最大隨機服務時間。9000
long serveTime=new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;//服務休息時間在1000到10000之間
try {
Thread.sleep(serveTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long costTime=System.currentTimeMillis()-beginTime;
System.out.println(windowName+"爲第"+num+"個"+"普通"+"客戶完成服務,耗時"+costTime/1000+"秒");
}else{
System.out.println(windowName+"沒有取到任務,先休息1秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void expressService() {
String windowName="第"+windowId+"號"+type+"窗口";
Integer num=NumberMachine.getInstance().getExpressManager().fetchServiceNumber();
System.out.println(windowName+"正在獲取任務");
if(num!=null){
System.out.println(windowName+"爲第"+num+"個"+type+"客戶服務!");
long beginTime=System.currentTimeMillis();
//int maxRand=Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;//最大隨機服務時間
//long serveTime=new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;//服務休息時間在1000到10000之間
try {
Thread.sleep(Constants.MIN_SERVICE_TIME);//快速服務休息的時間是最小值
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long costTime=System.currentTimeMillis()-beginTime;
System.out.println(windowName+"爲第"+num+"個"+type+"客戶完成服務,耗時"+costTime/1000+"秒");
}else{
System.out.println(windowName+"沒有取到任務");
commonService();//沒取到快速任務,獲取普通業務
}
}
private void vipService() {
String windowName="第"+windowId+"號"+type+"窗口";
Integer num=NumberMachine.getInstance().getVipManager().fetchServiceNumber();
System.out.println(windowName+"正在獲取任務");
if(num!=null){
System.out.println(windowName+"爲第"+num+"個"+type+"客戶服務!");
long beginTime=System.currentTimeMillis();
int maxRand=Constants.MAX_SERVICE_TIME-Constants.MIN_SERVICE_TIME;//最大隨機服務時間
long serveTime=new Random().nextInt(maxRand)+1+Constants.MIN_SERVICE_TIME;//服務休息時間在1000到10000之間
try {
Thread.sleep(serveTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long costTime=System.currentTimeMillis()-beginTime;
System.out.println(windowName+"爲第"+num+"個"+type+"客戶完成服務,耗時"+costTime/1000+"秒");
}else{
System.out.println(windowName+"沒有取到任務");
commonService();//沒取到VIP任務,獲取普通業務
}
}
});
}
}
public enum CustomerType {
COMMON,EXPRESS,VIP;
public String toString(){
switch(this){
case COMMON:
return "普通";
case EXPRESS:
return "快速";
case VIP:
return name();//枚舉對象的name方法就是返回自己的名字
}
return null;
}
}
public class Constants {//常量類
public static int MAX_SERVICE_TIME=10000;//最大服務時間
public static int MIN_SERVICE_TIME=1000;//最小服務時間
public static int COMMOON_CUSTOMER_INTERVAL_TIME=1;//普通客戶間隔時間
}
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=1;i<5;i++){
ServiceWindow commonwindow=new ServiceWindow();
commonwindow.setWindowId(i);
commonwindow.start();//啓動
}
ServiceWindow expresswindow=new ServiceWindow();
expresswindow.setType(CustomerType.EXPRESS);
expresswindow.start();
ServiceWindow vipwindow=new ServiceWindow();
vipwindow.setType(CustomerType.VIP);
vipwindow.start();
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(//不是普通線程池,而是調度線程池
new Runnable(){
public void run(){
Integer number=NumberMachine.getInstance().getCommonManager().generateNewManager();//生成號碼
System.out.println(number+"號普通客戶正在等待服務!");
}
},
0, //過0秒來第一個客戶
Constants.COMMOON_CUSTOMER_INTERVAL_TIME, //以後每隔多長時間來一個
TimeUnit.SECONDS);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
Integer number=NumberMachine.getInstance().getVipManager().generateNewManager();
System.out.println(number+"號VIP客戶正在等待服務!");
}
},
0,
Constants.COMMOON_CUSTOMER_INTERVAL_TIME*6,//VIP
TimeUnit.SECONDS);
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(
new Runnable(){
public void run(){
Integer number=NumberMachine.getInstance().getExpressManager().generateNewManager();
System.out.println(number+"號快速客戶正在等待服務!");
}
},
0,
Constants.COMMOON_CUSTOMER_INTERVAL_TIME*3,//快速
TimeUnit.SECONDS);
}
}
我的總結:我們掌握一定的面向對象設計思想。在學習銀行業務調度系統時,就不會容易出現哪些方法定義在哪裏的問題,也不會出現定義錯類的問題,也能很好地理解張老師講的思想。此外學習這個時,我們也應該要先掌握好多線程這個知識點。
---------------------- ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! ----------------------