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》,并发确实是个很难说的东西,再加上时间片本来分配的问题,那几个线程怎么可能确保同步呢。。。。

最后,看了下张老师的代码,。。。。。居然和我的路子不一样,我想的过于细了。结果给自己造了这么多绊子。就先这样吧,改天再深入研究研究并发。

原文地址:https://www.cnblogs.com/hangxin1940/p/2045390.html