筆者觀看了張老師關於銀行業務調度系統的視頻講解,按照要求自己重新編寫了程序。
1. 項目需求
(1)銀行內有6個業務窗口,1 - 4號窗口爲普通窗口,5號窗口爲快速窗口,6號窗口爲VIP窗口。
(2)有三種對應類型的客戶:VIP客戶,普通客戶,快速客戶(辦理如交水電費、電話費之類業務的客戶)。
(3)異步隨機生成各種類型的客戶,生成各類型用戶的概率比例爲:
VIP客戶 :普通客戶 :快速客戶 = 1 :6 :3。
(4)客戶辦理業務所需時間有最大值和最小值,在該範圍內隨機設定每個VIP客戶以及普通客戶辦理業務所需的時間,快速客戶辦理業務所需時間爲最小值(提示:辦理業務的過程可通過線程Sleep的方式模擬)。
(5)各類型客戶在其對應窗口按順序依次辦理業務。
(6)當VIP(6號)窗口和快速業務(5號)窗口沒有客戶等待辦理業務的時候,這兩個窗口可以處理普通客戶的業務,而一旦有對應的客戶等待辦理業務的時候,則優先處理對應客戶的業務。
(7)隨機生成客戶時間間隔以及業務辦理時間最大值和最小值自定,可以設置。
(8)不要求實現GUI,只考慮系統邏輯實現,可通過Log方式展現程序運行結果。
2. 需求分析
(2)點:創建三個用arraylist實現的隊列,分別爲普通客戶隊列,快速客戶隊列和VIP客戶隊列,相應客戶將被放到這些隊列裏;
(3)點:異步隨機生成三種類型的顧客,需要建立三個客戶生成線程,分別生成普通顧客,快速顧客和VIP顧客。在生成時檢驗一個隨機數是否處於某範圍內,若在則生成成功,若不在則生成失敗。三個生成線程的生成時間間隔是一樣的。比起張老師控制生成間隔來控制概率的方法,這個方法更符合生活實際,畢竟新老客戶出現的時間間隔不是固定的,只要在足夠長的時間內三種客戶的數量比例大體符合要求即可;
(4)點:客戶生成線程需要同時生成每個客戶的業務處理時間,該時間是指定範圍內的隨機數。當業務辦理流程取到客戶後,按照客戶的業務處理時間用線程休眠語句停頓對應時間;
(5)點:業務辦理線程需要從對應的等待隊列中取新客戶;
(6)點:快速業務辦理線程和VIP業務辦理線程在取客戶時要檢查本業務的等待隊列是否爲空。若爲空則要從普通客戶隊列裏取客戶;
(7)點:(3)點和(4)點即滿足;
(8)點:建立監視器線程,當三個等待隊列中的任何一個變化時,即打印出時間和變化後的情況。這樣每個客戶的出現時間,業務處理開始時間和結束時間都可以據此得出。
綜上,銀行業務調度系統的邏輯與交通燈系統有相似之處,但是也有一個很大的不同:銀行的多個窗口都在消耗同一個等待隊列,而交通燈系統中的一盞燈只是消耗一個等待隊列。因此要特別注意業務處理線程的串擾問題。爲了保證安全,需要用鎖鎖住其中的判斷和remove隊列語句。另外,在張老師提供的源碼中用到了工廠模式和java.util.concurrent包,這裏爲了簡便用的是常規方法生成對象和多線程。
3. 模塊分析
3.1 接口
import java.util.*;
//常數標籤
interface Tag
{
//顧客出現的時間間隔(秒)
public final double CUSTOMER_INTERVAL = 0.1;
//顧客所需服務時間最小值(秒)
public final double JOB_TIME_MIN = 0.4;
//最大值(秒)
public final double JOB_TIME_MAX = 1;
}
3.2 客戶類
//顧客類,包括每個顧客所需的服務時間
class Customer
{
double time;
public Customer(double time)
{
this.time = time;
}
}
3.3 等待隊列類
//等待隊列類,包括普通顧客的等待隊列,快速顧客的等待隊列和VIP顧客的等待隊列
class Queue
{
ArrayList<Customer> arrayNormal = new ArrayList<Customer>();
ArrayList<Customer> arrayFast = new ArrayList<Customer>();
ArrayList<Customer> arrayVip = new ArrayList<Customer>();
}
3.4 普通客戶生成類
class Normal implements Tag, Runnable
{
Queue queue;
public Normal(Queue queue)
{
this.queue = queue;
}
@Override
public void run()
{
long time = System.currentTimeMillis();
while(true)
{
int odd = new Random().nextInt(10);
//每次試圖產生普通顧客有60%成功率
if(odd < 6)
{
//放入普通客戶等待隊列
queue.arrayNormal.add(new Customer(JOB_TIME_MIN + (JOB_TIME_MAX - JOB_TIME_MIN) * new Random().nextDouble()));
}
//每次嘗試產生後停頓一段時間
try
{
Thread.sleep((long)(CUSTOMER_INTERVAL * 1000));
}
catch(Exception e){}
}
}
}
3.5 快速客戶生成類
//產生快速顧客的線程
class Fast implements Tag, Runnable
{
Queue queue;
public Fast(Queue queue)
{
this.queue = queue;
}
@Override
public void run()
{
while(true)
{
int odd = new Random().nextInt(10);
//每次試圖產生普通顧客有30%成功率
if(odd < 3)
{
//放入快速客戶等待隊列
queue.arrayFast.add(new Customer(JOB_TIME_MIN));
}
//每次嘗試產生後停頓一段時間
try
{
Thread.sleep((long)(CUSTOMER_INTERVAL * 1000));
}
catch(Exception e){}
}
}
}
3.6 VIP客戶生成類
//產生VIP客戶的線程
class Vip implements Tag, Runnable
{
Queue queue;
public Vip(Queue queue)
{
this.queue = queue;
}
@Override
public void run()
{
while(true)
{
//每次試圖產生普通顧客有10%成功率
int odd = new Random().nextInt(10);
if(odd < 1)
{
//放入VIP客戶等待隊列
queue.arrayVip.add(new Customer(JOB_TIME_MIN + (JOB_TIME_MAX - JOB_TIME_MIN) * new Random().nextDouble()));
}
//每次嘗試產生後停頓一段時間
try
{
Thread.sleep((long)(CUSTOMER_INTERVAL * 1000));
}
catch(Exception e){}
}
}
}
3.7 普通業務辦理類
//普通窗口的業務辦理線程
class NormalTask implements Tag, Runnable
{
Queue queue;
Object lock;
boolean wait;
double time;
public NormalTask(Queue queue, Object lock)
{
this.queue = queue;
this.lock = lock;
}
@Override
public void run()
{
while(true)
{
wait = false;
//remove動作要求公共鎖,防止其它線程干擾
synchronized(lock)
{
if(queue.arrayNormal.size() != 0)
{
//獲取該顧客辦理業務所需時間
time = queue.arrayNormal.get(0).time;
//從等待隊列中移除該顧客
queue.arrayNormal.remove(0);
wait = true;
}
}
if(wait)
{
try
{
//辦理業務耗時
Thread.sleep((long)(time * 1000));
}
catch(Exception e){}
}
}
}
}
3.8 快速業務辦理類
//快速窗口的業務辦理線程
class FastTask implements Tag, Runnable
{
Queue queue;
Object lock;
boolean wait;
double time;
public FastTask(Queue queue, Object lock)
{
this.queue = queue;
this.lock = lock;
}
@Override
public void run()
{
while(true)
{
wait = false;
synchronized(lock)
{
//優先辦理快速顧客業務
if(queue.arrayFast.size() != 0)
{
time = queue.arrayFast.get(0).time;
queue.arrayFast.remove(0);
wait = true;
}
//若快速顧客等待隊列爲空,則辦理普通顧客業務
else if(queue.arrayNormal.size() != 0)
{
time = queue.arrayNormal.get(0).time;
queue.arrayNormal.remove(0);
wait = true;
}
}
if(wait)
{
try
{
Thread.sleep((long)(time * 1000));
}
catch(Exception e){}
}
}
}
}
3.9 VIP業務辦理類
//VIP窗口的業務辦理線程
class VipTask implements Tag, Runnable
{
Queue queue;
Object lock;
boolean wait;
double time;
public VipTask(Queue queue, Object lock)
{
this.queue = queue;
this.lock = lock;
}
@Override
public void run()
{
while(true)
{
wait = false;
synchronized(lock)
{
//優先辦理VIP顧客業務
if(queue.arrayVip.size() != 0)
{
time = queue.arrayVip.get(0).time;
queue.arrayVip.remove(0);
wait = true;
}
//若VIP顧客等待隊列爲空,則辦理普通顧客業務
else if(queue.arrayNormal.size() != 0)
{
time = queue.arrayNormal.get(0).time;
queue.arrayNormal.remove(0);
wait = true;
}
}
if(wait)
{
try
{
Thread.sleep((long)(time * 1000));
}
catch(Exception e){}
}
}
}
}
3.10 監視器類
//監控器線程,當顧客等待隊列發生變化時打印當前等待隊列
class Monitor implements Tag, Runnable
{
Queue queue;
int normal_old = 0;
int fast_old = 0;
int vip_old = 0;
Object lock;
public Monitor(Queue queue, NormalTask normaltask1, NormalTask normaltask2, NormalTask normaltask3, NormalTask normaltask4, FastTask fasttask, VipTask viptask, Object lock)
{
this.queue = queue;
this.lock = lock;
}
@Override
public void run()
{
long time = System.currentTimeMillis();
while(true)
{
synchronized(lock)
{
if(queue.arrayNormal.size() != normal_old || queue.arrayFast.size() != fast_old || queue.arrayVip.size() != vip_old)
{
System.out.println((double)(System.currentTimeMillis() - time)/1000 + "s");
System.out.println("Normal Fast VIP");
System.out.println(queue.arrayNormal.size() + " " + queue.arrayFast.size() + " " + queue.arrayVip.size());
System.out.println("");
normal_old = queue.arrayNormal.size();
fast_old = queue.arrayFast.size();
vip_old = queue.arrayVip.size();
}
}
}
}
}
3.11 main類
public class Bank
{
public static void main(String[] args)
{
//所有業務辦理流程共享的鎖
Object lock = new Object();
Queue queue = new Queue();
Normal normal = new Normal(queue);
Fast fast = new Fast(queue);
Vip vip = new Vip(queue);
NormalTask normaltask1 = new NormalTask(queue, lock);
NormalTask normaltask2 = new NormalTask(queue, lock);
NormalTask normaltask3 = new NormalTask(queue, lock);
NormalTask normaltask4 = new NormalTask(queue, lock);
FastTask fasttask = new FastTask(queue, lock);
VipTask viptask = new VipTask(queue, lock);
//開啓顧客生成線程
new Thread(normal).start();
new Thread(fast).start();
new Thread(vip).start();
//開啓業務辦理線程
new Thread(normaltask1).start();
new Thread(normaltask2).start();
new Thread(normaltask3).start();
new Thread(normaltask4).start();
new Thread(fasttask).start();
new Thread(viptask).start();
//開啓監控器線程
new Thread(new Monitor(queue, normaltask1, normaltask2, normaltask3, normaltask4, fasttask, viptask, lock)).start();
}
}