多线程模拟银行业务调度系统

一、项目业务逻辑分析

    项目需求:模拟实现银行业务调度系统逻辑,要求如下:

  • 银行内有6个业务窗口,1-4号为普通窗口,5号为快速窗口,6号为VIP窗口。

  • 对应3中类型的客户:普通客户,VIP客户,快速客户(办理如交水电费,电话费之类的业务)

  • 异步随机生成各种各类型的客户,比例VIP:普通客户:快速客户:1:6:3。

  • 客户办理业务所需时间有最大值和最小值,普通客户和VIP客户办理业务所需时间再两者之间,快速客户取最小值。

  • 当VIP窗口和快速窗口没有客户等待办理业务时,这两个窗口可以处理普通客户的业务。

    该题目的业务逻辑和交通灯管理系统有很多相似之处,都是基于多线程并发执行的业务逻辑,至少需要2个线程,业务窗口线程:负责即时检测有没有对应的客户在排队,VIP窗口和快速窗口在没有对应客户排队的情况下,可以处理普通客户的业务。生成客户线程:每个一定的时间就产生一个不同类型的客户。在现实的银行营业厅中都有一个排好机,选择业务类型就会打印出一个排队号码,这里我们也模拟设计一个排号机,负责客户排队。

二、系统详细设计

根据业务需求分析,设计这样几个对象:服务窗口对象,客户对象,排号机对象。下面具体分析每个对象所以属性和方法。

服务窗口对象:应该有窗口类型和窗口编号两个属性。提供3个方法,用于检测3种类型的客户是否又在排队的。

客户对象:这里系统主要是银行的业务逻辑,客户不需要具体化,用一个数字或字符串表示就行了。但是需要一个客户类型对象来表示不同的客户,这里用枚举类型实现。

排号机对象:对办理业务的客户排号的前提是先有客户,所以排号机兼负产生模拟客户的人物,产生一个客户也就是有一个客户进入对应类型的排队排列。

主要类图如下:

具体实现和业务逻辑分析略有不同,但整体设计是相同的。

三、具体实现

NumberManager.java

public class NumberManager {
	private int lastNumber = 0;
	//客戶排队队列
	private List<Integer> queueNumbers = new ArrayList<Integer>();
	//随机产生模拟客户
	public synchronized Integer generateNewNumber(){
		queueNumbers.add(++lastNumber);
		return lastNumber;
	}
	//服务窗口获取下一个客户
	public synchronized Integer fetchNumber(){
		if(queueNumbers.size()>0){
			return (Integer)queueNumbers.remove(0);
		}else{
			return null;
		}
	}
}

ServiceWindow.java

public class ServiceWindow {
	// 根据客户类型获取下一个客户
	private CustomerType type = null;
	// 服务窗口编号
	private int windowId = 1;

	public ServiceWindow(CustomerType type, int windowId) {
		super();
		this.type = type;
		this.windowId = windowId;
	}

	public void start() {
		Executors.newSingleThreadExecutor().execute(new Runnable() {

			@Override
			public void run() {
				do{
					switch (type) {
					case COMMON:
						commonService();
						break;
					case VIP:
						expressService();
						break;
					case EXPRESS:
						vipService();
						break;
						
					default:
						break;
					}
				}while(true);
			}

		});
	}

	private void commonService() {
		String windowName = windowId + "号普通窗口";
		// 尝试从客户排队队列中获取下一个客户
		System.out.println(windowName + " 正在获取任务。。。");
		Integer serviceNum = NumberMachine.getInstance().getCommonManager()
				.fetchNumber();
		if (serviceNum != null) {
			// 用sleep模拟客户服务时间
			System.out.println(windowName + "开始为" + serviceNum + "号普通客户服务");
			int max_Random = Constants.MAX_SERVICE_TIME
					- Constants.MIN_SERVICE_TIME;
			long serviceTime = new Random().nextInt(max_Random) + 1+Constants.MIN_SERVICE_TIME;
			try {
				Thread.sleep(serviceTime);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(windowName + "为 " + serviceNum
					+ " 号普通客户服务完成,用时:" + serviceTime / 1000 + " 秒");
		} else {
			System.out.println(windowName + " 没有获取到客户,等待中。。。");
			try {// 等待1秒
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

CustomerType.java

public enum CustomerType {
	VIP,EXPRESS,COMMON;
	public String toString(){
		switch (this) {
		case VIP:
			return "VIP";
		case EXPRESS:
			return "快速";
		case COMMON:
			return "普通";
		}
		return null;
	}
}

NumberMachine.java

public class NumberMachine {
	
	private static NumberMachine instance = new NumberMachine();
	//普通客戶排号机
	private NumberManager commonManager = new NumberManager();
	//快速客戶排号机
	private NumberManager expressManager = new NumberManager();
	//VIP客戶排号机
	private NumberManager vipManager = new NumberManager();
	//单例模式创建一个号码管理器
	private NumberMachine(){}
	
	public static NumberMachine getInstance(){
		return instance;
	}
	public NumberManager getCommonManager() {
		return commonManager;
	}
	public NumberManager getExpressManager() {
		return expressManager;
	}
	public NumberManager getVipManager() {
		return vipManager;
	}	
}

原文地址:https://www.cnblogs.com/lukeguo/p/8824777.html