需求:
银行业务调度系统
模拟实现银行业务调度系统逻辑,具体需求如下:
Ø 银行内有 6 个业务窗口, 1 - 4 号窗口为普通窗口, 5 号窗口为快速窗口, 6 号窗口为 VIP 窗口。
Ø 有三种对应类型的客户: VIP 客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。
Ø 异步随机生成各种类型的客户,生成各类型用户的概率比例为:
VIP 客户 :普通客户 :快速客户 = 1 : 6 : 3 。
Ø 客户办理业务所需时间有最大值和最小值,在该范围内随机设定每个 VIP 客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间为最小值(提示:办理业务的过程可通过线程 Sleep 的方式模拟)。
Ø 各类型客户在其对应窗口按顺序依次办理业务。
Ø 当 VIP ( 6 号)窗口和快速业务( 5 号)窗口没有客户等待办理业务的时候,这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务的时候,则优先处理对应客户的业务。
Ø 随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
Ø 不要求实现 GUI ,只考虑系统逻辑实现,可通过 Log 方式展现程序运行结果。
看了需求后,我第一个想到的就是生产者和消费者这个经典面试题
下面来看下以前我写的生产消费者DEMO:
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
//NEW一个装馒头的框框
Producer p = new Producer(ss);
//NEW一个生产者,他的构造函数有框框,所以把上面NEW的装馒头的框框传进来,为啥子构造函数有框框,看下面
Consumer c = new Consumer(ss);
//NEW一个消费者,同上。
new Thread(p).start();
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
//3个生产者线程,一个消费者线程
}
}
class WoTou {
//馒头类
int id;
WoTou(int id) {
this.id = id;
}
//构造方法
public String toString() {
return "WoTou : " + id;
}
//TOSTRING方法返回ID
}
class SyncStack {
int index = 0;
WoTou[] arrWT = new WoTou[6];
//申明他是装(馒头)的框框,而且数组的下表只能是6个,不能越界,否则就要报ArrayIndexOutOfBounds数组下标越界异常,所以意思就是容量是6,只能装6个最多
public synchronized void push(WoTou wt) {
//框框有个方法,就是往里面放馒头 synchronized是上锁的意思,意思就是锁住这个放东西的方法
//为啥子要锁住?因为不可能我放馒头的时候同时有人在吃馒头之类的,这样逻辑上就会出错,到底是我放进去了还是吃进去了?所以要锁住他
while(index == arrWT.length) {
//当装的馒头是框框的最大容量的时候
try {
this.wait();
//就线程等待
} catch (InterruptedException e) {
e.printStackTrace();
//否则抛异常
}
}
this.notifyAll();
//正因为上面等待了,所以反之还要叫醒其他线程,不然有可能情况就是大家都在等了,等死求了
arrWT[index] = wt;
//馒头的数量等于传进去的馒头
index ++;
//你懂的
}
public synchronized WoTou pop() {
//同上面,有放就有吃
while(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
class Producer implements Runnable {
//生产者类,实现了Runnable接口
SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
//生产者有个方法,就是生产馒头这个方法
for(int i=0; i<20; i++) {
//这规定他只能生产20个馒头
WoTou wt = new WoTou(i);
ss.push(wt);
//调用PUSH方法
System.out.println("生产了:" + wt);
try {
Thread.sleep((int)(Math.random() * 200));
//每生产一个,就睡一会,时间是随机数
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
//同生产者,差不多
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
当时注释是写给一个学C#的朋友看的,怕他看不懂JAVA的语法,所以注释写的很通俗...
再看看一个业务加强的生产消费者DEMO:
public class Test1 {
/**
* @param args
*/
public static void main(String[] args) {
//如有多出生产此处q必须为单例模式,如最后那个源文件!
QueueThread q = new QueueThread();
// 启动10条消费线程
for (int i = 0; i < 5; i++) {
Consumer c = new Consumer(q);
c.start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// for (int i = 0; i < 50; i++) {
Producer p = new Producer(q);
p.setStr("生产线程产生ggggggggg ");
p.start();
// }
// 非生产线程产生数据
for (int i = 0; i < 50; i++) {
q.put(" 非生产线程产生数据 " + i);
}
}
}
/// QueueThread.java 线程池
import java.util.LinkedList;
public class QueueThread {
final static int MaxLength = 10;// 等待队列大小
private static LinkedList thread_pool = null;
public QueueThread() {
synchronized (this) {
if (thread_pool == null) {
System.out.println("初始化");
thread_pool = new LinkedList();
}
}
}
/**
* 判断是否已满
*
* @return
*/
public boolean isFull() {
return thread_pool.size() >= MaxLength ? true : false;
}
/**
* 判断是否为空
*
* @return
*/
public boolean isEmpty() {
return thread_pool.isEmpty();
}
/**
* 生产模式:插入数据到队列
*
* @param obj
*/
public synchronized void put(Object obj) {
while (isFull()) {
try {
System.out.println("满了等待。。。。。。");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果没满,插入
// 插入到队尾
System.out.println("没满。。。。。");
thread_pool.addLast(obj);
notify();// notifyAll();?
}
/**
* 消费模式:从队列里面取出一个对象
*
* @return
*/
public synchronized Object get() {
// 如果是空的则等待
while (isEmpty()) {
System.out.println("没数据等待。。。。。");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("有数据,干活。。。。。");
notify();
return thread_pool.removeFirst();
}
}
/ Consumer.java
/**
* 消费线程
*
* @author Mygia.mai
*
*/
public class Consumer extends Thread {
private QueueThread q;
public Consumer(QueueThread q){
this.q=q;
}
public void run() {
while (true) {
System.out.println(getName() + " 开始执行消费!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(getName() + q.get());
System.out.println(getName() + " 消费结束!");
}
}
}
// Producer.java 生产者线程
public class Producer extends Thread {
QueueThread q;
private String str;
Producer(QueueThread q) {
this.q = q;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public void run() {
for (int i = 0; i < 50; i++)
q.put(str + i);
}
}
// InitQueue.java 初始化消费线程池
public class InitQueue {
private static QueueThread queue = null;
public InitQueue() {
synchronized (this) {
if (queue == null) {
System.out.println("初始化消费线程队列!启动5条");
queue = new QueueThread();
// 启动5条消费线程
for (int i = 0; i < 5; i++) {
Consumer c = new Consumer(queue);
c.start();
}
}
}
}
/**
* 获取
* @return
*/
public QueueThread getQueue() {
return queue;
}
}
在这里,银行业务系统的业务逻辑只是比上面的稍做变化的更复杂一点,
客户就像是生产者生产的馒头,生产者不停的按照1:6:3的比例生成3种馒头
银行的窗口就是消费者,不过这里有3种消费者,分别有各自特殊的消费的方法,
像需求的后几条内容,比如VIP和快速窗口,比如每个线程消费者消费的时间,其实应该是在整个系统流程走通的情况下往里面添加额外的功能再来实现
但是总体来说大体一致。