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》,并发确实是个很难说的东西,再加上时间片本来分配的问题,那几个线程怎么可能确保同步呢。。。。
最后,看了下张老师的代码,。。。。。居然和我的路子不一样,我想的过于细了。结果给自己造了这么多绊子。就先这样吧,改天再深入研究研究并发。