多线程银行业务办理模拟

1. 项目介绍

设计一个多线程模拟银行业务办理程序:

  • 业务时间

  模拟客户可以随机办理银行提供的8种业务中的一种(基准时间自定义),办理时间规划为:

业务业务序号办理时间范围
取款10.5-1.5r
存款20.5-1.5r
缴纳罚款31.2-2.0r
开通网银45.0-8.0r
缴纳水电费51.5-2.0r
购买基金62.0-3.0r
转账汇款73.0-4.0r
个贷还款82.0-4.0r
  r=基准时间
  • 服务窗口

  程序模拟多个窗口服务,窗口服务具有一定的差异化,窗口开放情况:

窗口类型可办理业务办理客户范围
A类窗口1,2,3,4,5,6,7,8所有客户
B类窗口1,2,4,5,7所有客户
C类窗口1,2,3,4,5,6,7,8VIP优先(所有客户)
  模拟开放为A类:B类:C类=1:2:1
  • 模拟客户

  客户区分普通客户与VIP客户
  客户模拟需随机产生

  • 展示客户列表

  模拟时间:8:00-17:00
  结束营业后,打印当天客户列表
  客户列表包含:客户名称客户到达时间客户办理业务类型客户所用时间

  • 统计数据

  当天客户平均业务办理时间(客户办理时间:到达——完成
  当天客户所有办理业务中的所占比例

  • 仿真性要求(评优要求)

  参数化设定当天客户办理业务的比例(比如设置业务1占20%)
  提供窗口开放建议:根据当日的业务情况给出建议的A、B、C类三窗口数量,达到与基准日一致的客户服务时间


2. 考核要求

  • 基本要求:

(1)提交规范的代码
(2)系统包含多线程线程互斥线程同步
(3)业务类型使用Enum类型
(4)程序所有存储都需要使用集合,不能使用数组

  • 设计核心问题:

(1)程序整体结构设计
(2)包含有营业过程管理:开门关门营业分配过程
(3)实现用户模拟
(4)实现窗口分配模拟
(5)仿真的方法(设计仿真的方法)

  • 项目上交:

(1)上交完整工程代码
(2)相关说明文档


3. 主要功能

  • 实现营业过程(开门、关门、营业分配)
  • 实现随机生成用户及需办理业务
  • 实现合理窗口分配
  • 实现多线程、线程同步、线程互斥
  • 实现计算当天办理平均时间、当天业务占比
  • 实现参数化分配业务
  • 实现计算窗口开放建议

4. 仿真性比例自定义

  • 业务比例定义

业务业务序号自定义
取款120%
存款215%
缴纳罚款310%
开通网银410%
缴纳水电费510%
购买基金610%
转账汇款715%
个贷还款810%
  • VIP客户比例定义

| 客户 | 自定义 |
| :-: | :-: | :-: |
| VIP客户 | 15% |
| 普通客户 | 75% |


5. 类职责划分与核心代码

  • Bank_Business_Enum类:

(1)枚举类定义(与普通类不一样,需要提前固定)
public enum Bank_Business_Enum {

      //实际类一般采用大写
	WITHDRAW("取款服务",1,0.5,1.5,20.00),
	DEPOSIT("存款服务",2,0.5,1.5,15.00),
	PAY_FINE("缴纳罚款",3,1.2,2.0,10.00),
	OPEN_ONLINE_BANK("开通网银",4,5.0,8.0,10.00),
	PAY_WATER_ELECTRICITY("缴纳水电",5,1.5,2.0,10.00),
	PURCHASE_FUND("购买基金",6,2.0,3.0,10.00),
	REMITTANCE("转账汇款",7,3.0,4.0,15.00),
	REPAYMENT("个贷还款",8,2.0,4.0,10.00)

(2)数据定义

      //创建数据定义
	private String Business_Name;//业务名
	private int Business_ID;//业务ID
	private double Business_MinTime;//默认业务最小时间范围
	private double Business_MaxTime;//默认业务最小时间范围
	private double default_proporion=0;//默认定义占比
	private double proportion=0;//实际占比

(3)方法构造器

      //银行业务枚举类方法创建
	Bank_Business_Enum(String Business_Name,int Business_ID,double Business_MinTime,double Business_MaxTime,double Default_Proporion){
		this.Business_Name=Business_Name;
		this.Business_ID=Business_ID;
		this.Business_MaxTime=Business_MaxTime;
		this.Business_MinTime=Business_MinTime;
		this.default_proporion=Default_Proporion;
	}

(4)枚举类型需要尽可能少的定义get方法,保证枚举数据的安全

        //get方法已省略
	public void setProportion(double proportion) {
		this.proportion = proportion;
	}
  • Bank_Business_Record类:

(1)业务办理记录类数据定义
public class Bank_Business_Record implements Comparable<Bank_Business_Record> {

	public long arrive_time;//到达时间戳
	public long business_time;//业务办理时间戳
	public long finish_time;//结束时间戳
	public int index;//取票号码
	public int bussiness_id;//办理业务id
	public String business_name;//办理业务名
	public String windows_name;//窗口名
	public int windows_ID;//窗口id
	public char windows_type;//窗口类型
	public int customer_ID;//客户id
	public boolean if_VIP;//客户是否为VIP	

(2)重写排序方法,按照取号码升序进行Collection.sort排序

    @Override
    public int compareTo(Bank_Business_Record o) {
        if(this.index < o.index){
            return -1; 
        }else if(this.index == o.index){
            return 0;
        }else{
            return 1;
        }
    }

(3)get、set方法

    //方法已省略
  • Bank_Customer类:

(1)客户类数据定义
public class Bank_Customer {

	private int Customer_ID;//客户ID
	private int Customer_Business_ID;//客户办理的业务ID
	private int Customer_Index;//客户排队叫号码,因为时间问题,将客户ID默认为排队叫号码
	private long Arrive_Time;//客户到达时间
	private long Business_Time;//客户业务办理时间,由Bank_Random类里的方法生成随机时间
	private long Finish_Time;//客户完成业务时间
	private boolean If_VIP;//客户是否为VIP

(2)方法构造器

	//客户构造器
	public Bank_Customer(int customer_id,int business_id,int index,long arrive_time,boolean if_VIP) {
		Customer_ID=customer_id;
		Customer_Business_ID=business_id;
		Customer_Index=index;
		Arrive_Time=arrive_time;
		If_VIP=if_VIP;
	}

(3)get、set方法

    //方法已省略
  • Bank_Windows类:

(1)窗口类数据定义

	private String Windows_Name;//窗口名
	private int Windows_ID;//窗口ID
	private char Windows_Type;//窗口类型:A、B、C
	private ArrayList<Integer> Windows_Business;//窗口支持服务集
	private boolean If_WindowsFor_VIP;//窗口是否为VIP专窗(即C类窗口)
	private long business_end_time=0l;//窗口业务结束时间(即窗口是否Open,根据时间流动与其进行比较,若达到指定服务时间,打开窗口叫号新客户)

(2)方法构造器

	//银行窗口构造函数
	public Bank_Windows(String windows_Name,char type,int windows_ID,boolean if_WindowsFor_VIP){
		Windows_Name = windows_Name;
		Windows_Type = type;
		Windows_ID = windows_ID;
		Windows_Business = new ArrayList<Integer>();
		If_WindowsFor_VIP=if_WindowsFor_VIP;
		switch (type) {
			case 'A':
				for(int i=1;i<9;i++)
					Windows_Business.add(i);
				break;
				
			case 'B':
				Windows_Business.add(1);
				Windows_Business.add(2);
				Windows_Business.add(4);
				Windows_Business.add(5);
				Windows_Business.add(7);
				break;
				
			case 'C':
				for(int i=1;i<9;i++)
					Windows_Business.add(i);
				break;
				
			default:
				break;
		}
	}

(3)get、set方法

    //方法已省略
  • Bank_Random类:

public class Bank_Random {
(1)按照固定比例随机生成业务的方法

	public static int random_business() {
		int key=0;
		int value=0;
		key=(int)(Math.random()*100+0);
		if (key<20) {
			value=1;
		} else if(key<35) {
			value=2;
		} else if(key<45) {
			value=3;
		} else if(key<55) {
			value=4;
		} else if(key<65) {
			value=5;
		} else if(key<75) {
			value=6;
		} else if(key<90) {
			value=7;
		} else if(key<100) {
			value=8;
		}else {
			value=1;
		}
		return value;
	}

(2)按照固定比例随机分配VIP的方法

	public static boolean random_VIP() {
		int key=0;
		boolean value=false;
		key=(int)(Math.random()*100+0);
		if (key<15) {
			value=true;
		}else {
			value=false;
		}
		return value;
	}

(3)按照固定比例随机生成业务所需时间的方法

	public static long random_time(double min,double max) {
		long value=(long)(Math.random()*1.0*((max-min)*Main_Util.R)+1.0*Main_Util.R*min);
		return value;
	}
  • Main_Util类:

(1)import集

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

(2)加锁,实现同步与互斥
public class Main_Util {

	//叫号机在消费与生产时需要互斥
	public static Lock lock =new ReentrantLock();//确保锁住叫号机
	//时间在写入与读取时需要互斥
	public static boolean timerun=false;//时间锁

(3)创建全局int参数

	public static int index = 1;//叫号机号码
	public static int served_normal = 0;//服务普通客户计数
	public static int served_VIP = 0;//服务VIP客户计数

(4)创建输出格式

	public final static SimpleDateFormat dft = new SimpleDateFormat ("yyyy.MM.dd HH:mm:ss");//时间格式定义
	public final static DecimalFormat double2 = new DecimalFormat("######0.00");//输出保留2位小数

(5)创建全局时间(long)参数

	public final static long R=600000l;//设置单位时间R=10分钟=60*10*1000l
	public static long Open_time = 0l;//时间戳初始化
	public static long Total_time = 0l;//默认业务一直不停,时间跑动时间通过业务时间计算
	public static long Close_time=0l;//关门时间
	public static long Waited_time=0l;//累计等候+完成业务时间(算平均)
	public static long wait_A_time=0l;//A窗口的累计等候时间
	public static long wait_B_time=0l;//B窗口的累计等候时间
	public static long wait_C_time=0l;//C窗口的累计等候时间
	public static long Served_time=0l;//累计业务服务时间(算平均)
	public static long serve_A_time=0l;//A窗口的累计业务服务时间
	public static long serve_B_time=0l;//B窗口的累计业务服务时间
	public static long serve_C_time=0l;//C窗口的累计业务服务时间

(6)创建窗口实体

	public static Bank_Windows A1Windows=new Bank_Windows("A1",'A',1,false);
	public static Bank_Windows B1Windows=new Bank_Windows("B1",'B',1,false);
	public static Bank_Windows B2Windows=new Bank_Windows("B2",'B',2,false);
	public static Bank_Windows C1Windows=new Bank_Windows("C1",'C',1,true);

(7)创建记录业务次数HashMap、查找业务映射HashMap

	public static HashMap<Integer,Integer> business_statistic_map=new HashMap<Integer,Integer>();//记录业务次数
	public static HashMap<Integer, Bank_Business_Enum> business_enum_map=new HashMap<Integer, Bank_Business_Enum>();//映射方便查找业务

(8)创建记录客户、业务记录的ArrayList

	public static ArrayList<Bank_Customer> customerList=new ArrayList<Bank_Customer>();//存储等候普通与VIP用户
	public static ArrayList<Bank_Customer> VIP_customerList=new ArrayList<Bank_Customer>();//存储等候的VIP用户
	public static ArrayList<Bank_Business_Record> recordList=new ArrayList<Bank_Business_Record>();//业务记录表
	public static ArrayList<Bank_Business_Record> recordListA=new ArrayList<Bank_Business_Record>();
	public static ArrayList<Bank_Business_Record> recordListB=new ArrayList<Bank_Business_Record>();
	public static ArrayList<Bank_Business_Record> recordListC=new ArrayList<Bank_Business_Record>();

(9)Main函数,开业
public static void main(String[] args) throws Exception{

	        System.out.println("欢迎使用银行业务模拟办理(计算1802 BK阿码农)");

		//获取业务枚举,默认取款业务
		business_enum_map.clear();
		business_enum_map.put(1,Bank_Business_Enum.WITHDRAW);
		business_enum_map.put(2,Bank_Business_Enum.DEPOSIT);
		business_enum_map.put(3,Bank_Business_Enum.PAY_FINE);
		business_enum_map.put(4,Bank_Business_Enum.OPEN_ONLINE_BANK);
		business_enum_map.put(5,Bank_Business_Enum.PAY_WATER_ELECTRICITY);
		business_enum_map.put(6,Bank_Business_Enum.PURCHASE_FUND);
		business_enum_map.put(7,Bank_Business_Enum.REMITTANCE);
		business_enum_map.put(8,Bank_Business_Enum.REPAYMENT);

		//初始化业务
		business_statistic_map.clear();
		for(int i=1;i<9;i++) {
			business_statistic_map.put(i,0);
		}
		
		//获取当天日期
		Open_time+=System.currentTimeMillis();//获取当前时间戳
		Open_time-=(System.currentTimeMillis()+1000*3600*8)%(86400*1000);//消除东八区干扰,减去时分秒,获取东八区当天00:00:00的时间戳
		Open_time+=1000*3600*8;//获取日期后加8小时达到开门时间
		
		//创建线程:产生顾客
		Customer_Make customer_make=new Customer_Make();
		Thread customer_make_thread =new Thread(customer_make);

		//创建线程:产生A类窗口
		Windows_ServerA window_a1=new Windows_ServerA(A1Windows);
		Thread window_a1_thread=new Thread(window_a1);

		//创建线程:产生B类窗口
		Windows_ServerB window_b1=new Windows_ServerB(B1Windows);
		Thread window_b1_thread=new Thread(window_b1);
		Windows_ServerB window_b2=new Windows_ServerB(B2Windows);
		Thread window_b2_thread=new Thread(window_b2);

		//创建线程:产生C类窗口
		Windows_ServerC window_c1=new Windows_ServerC(C1Windows);
		Thread window_c1_thread=new Thread(window_c1);

		//创建线程:时间流动
		Time_Make times=new Time_Make();
		Thread time_thread=new Thread(times);
		
		//开业操作
		System.out.println("开业时间:"+dft.format(Open_time));

		//开启窗口、客户生产机、时间线程
		window_a1_thread.start();
		window_b1_thread.start();
		window_c1_thread.start();
		window_b2_thread.start();
		customer_make_thread.start();
		time_thread.start();

(10)Main函数,结束营业

		//判断线程结束
		while(true) {
			if(!customer_make_thread.isAlive())break;
		}
		while(true) {
			if(!window_a1_thread.isAlive())break;
		}
		while(true) {
			if(!window_b1_thread.isAlive())break;
		}
		while(true) {
			if(!window_c1_thread.isAlive())break;
		}
		while(true) {
			if(!window_b2_thread.isAlive())break;
		}
		//停止时间流动
		time_thread.stop();

		//结业操作
		System.out.println("结业时间:"+dft.format(Close_time));

(11)Main函数,分析操作

		//创建读取业务记录实例
		Bank_Business_Record record_demo=new Bank_Business_Record();
		//不开源
		//输出服务人数与VIP占比
		//输出各业务占比
		//输出累计服务时间与各类型窗口服务时间
		//输出累计等候时间与各类型窗口等候时间
		//设置默认窗口数
		//分析操作
		System.out.println("综上,建议开窗口个数为:A窗口"+A_win+"个,B窗口"+B_win+"个,C窗口"+C_win+"个");

(12)Main函数,打印(不开源)

		System.out.println("\n总客户列表");
		System.out.println("客户序号\t办理窗口\t业务序号\t办理业务\t业务时间\t到达时间\t\t\t开始办理时间\t\t办理完成时间");
		for(int i=0;i<recordList.size();i++) {
			
		}
		//打印A类窗口客户列表(根据取号大小排序)
		System.out.println("\n客户列表A");
		System.out.println("客户序号\t办理窗口\t业务序号\t办理业务\t业务时间\t到达时间\t\t\t开始办理时间\t\t办理完成时间");
		Collections.sort(recordListA);
		for(int i=0;i<recordListA.size();i++) {
			
		}
		//打印类B窗口客户列表(根据取号大小排序)
		System.out.println("\n客户列表B");
		System.out.println("客户序号\t办理窗口\t业务序号\t办理业务\t业务时间\t到达时间\t\t\t开始办理时间\t\t办理完成时间");
		Collections.sort(recordListB);
		for(int i=0;i<recordListB.size();i++) {
			
		}
		//打印类C窗口客户列表(根据取号大小排序)
		System.out.println("\n客户列表C");
		System.out.println("客户序号\t办理窗口\t业务序号\t办理业务\t业务时间\t到达时间\t\t\t开始办理时间\t\t办理完成时间");
		Collections.sort(recordListC);
		for(int i=0;i<recordListC.size();i++) {
			
		}

(13)Time_Make时间流动方法
public static class Time_Make implements Runnable{

		@Override
		public void run() {
			while(xxxxxxxxxxx) {
				if(xxxxxxxxxxx) {
					xxxxxxxxxxxx;
				}
				try {
					Thread.sleep(xxxxxxxxxxx); 
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

(14)随机生成客户方法
public static class Customer_Make implements Runnable{

		@Override
		public void run() {
			while(Total_time<=32400000l) {//设置时间(时间戳17:00截止,共计9小时=9*3600*1000)
				Bank_Customer customer=new Bank_Customer(index,Bank_Random.random_business(), index, Open_time+Total_time, Bank_Random.random_VIP());
				int id=customer.getCustomer_Business_ID();
				lock.lock();
				if(customer.isIf_VIP()) {
					customerList.add(customer);
					VIP_customerList.add(customer);
					System.out.println("【取票】\t"+dft.format(Open_time+Total_time)+"\t"
					+index+"号 VIP客户 取票成功!\t等待办理:“"+business_enum_map.get(id).getBusiness_Name()+"”\t当前VIP队伍人数:"+(VIP_customerList.size())+"人");
				}else {
					customerList.add(customer);
					System.out.println("【取票】\t"+dft.format(Open_time+Total_time)+"\t"
					+index+"号 普通客户 取票成功!\t等待办理:“"+business_enum_map.get(id).getBusiness_Name()+"”\t当前队伍人数:"+(customerList.size())+"人");
				}
				index++;
				lock.unlock();
				try {
					Thread.sleep(xxxxxxxxxxx);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

(15)A窗口办理业务方法
public static class Windows_ServerA implements Runnable{

		public Bank_Windows windows;
		public Windows_ServerA(Bank_Windows windows) {
			this.windows=windows;
		}
		@Override
		public void run() {
			while(true) {
				
				if(Total_time>=32400000l && customerList.isEmpty()) {
					break;
				}
				else if((Total_time+Open_time)<windows.getBusiness_end_time()) {
					try {		
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					continue;
				}
				
				lock.lock();
				timerun=false;
				if(!customerList.isEmpty()) {
					
					Bank_Customer customer =new Bank_Customer();
					long business_time;
					long start_time;
					int business_id;
					
					customer=customerList.get(0);//获取下一个用户信息
					start_time=Total_time+Open_time;
					
					if(customer.isIf_VIP()){//VIP用户拥有两条队伍
						customerList.remove(customer);
						VIP_customerList.remove(customer);
					}else {//普通用户只有一条队伍
						customerList.remove(customer);
					}
					lock.unlock();
					
					business_id=customer.getCustomer_Business_ID();
					business_time=Bank_Random.random_time(business_enum_map.get(business_id).getBusiness_MinTime(), business_enum_map.get(business_id).getBusiness_MaxTime());//生成随机业务时间
					customer.setBusiness_Time(business_time);
					
					System.out.println("【叫号】\t"+dft.format(start_time)+"\t请"+customer.getCustomer_ID()+"预计时间"+double2.format(1.0*customer.getBusiness_Time()/60000)+"分");
					
					try {		
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					
					business_statistic_map.put(business_id, business_statistic_map.get(business_id)+1);
					customer.setFinish_Time(start_time+business_time);
					windows.setBusiness_end_time(start_time+business_time);
					Served_time+=business_time;//加入累计总时间
					serve_A_time+=business_time;//加入累计A时间
					Waited_time+=(customer.getFinish_Time()-customer.getArrive_Time());//加入累计总时间
					wait_A_time+=(customer.getFinish_Time()-customer.getArrive_Time());//加入累计A时间
					
					//记录数据
					Bank_Business_Record record=new Bank_Business_Record();
					record.setArrive_time(customer.getArrive_Time());
					record.setFinish_time(customer.getFinish_Time());
					record.setIndex(customer.getCustomer_Index());
					record.setBussiness_id(business_id);
					record.setBusiness_name(business_enum_map.get(business_id).getBusiness_Name());
					record.setWindows_ID(windows.getWindows_ID());
					record.setWindows_name(windows.getWindows_Name());
					record.setWindows_type(windows.getWindows_Type());
					record.setCustomer_ID(customer.getCustomer_ID());
					record.setIf_VIP(customer.isIf_VIP());
					record.setBusiness_time(customer.getBusiness_Time());
					recordList.add(record);
					recordListA.add(record);
					if(!customer.isIf_VIP()) {
						served_normal++;
					}else {
						served_VIP++;
					}
					System.out.println("【完成】\t"+customer.getCustomer_ID()+"号客户将于"+dft.format(customer.getFinish_Time())+"在"+windows.getWindows_Name()+"窗口完成“"+record.business_name+"”业务");	
					Close_time=customer.getFinish_Time();
					timerun=true;
					
				} else {
					//System.out.println("【空闲】\t"+dft.format(Open_time+Total_time)+"\t"+windows.getWindows_Name()+"窗口空闲\t等待客户中");
					lock.unlock();
					try {
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					timerun=true;
					continue;
				}
				
			}
		}

(16)B窗口办理业务方法
public static class Windows_ServerB implements Runnable{

		public Bank_Windows windows;
		public Windows_ServerB(Bank_Windows windows) {
			this.windows=windows;
		}
		@Override
		public void run() {
			while(true) {
				if(Total_time>=32400000l && customerList.isEmpty()) {
					break;
				}
				else if((Total_time+Open_time)<windows.getBusiness_end_time()) {
					try {		
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					continue;
				}
				lock.lock();
				timerun=false;
				if(!customerList.isEmpty()) {
					Bank_Customer customer =new Bank_Customer();
					long business_time;
					long start_time;
					int business_id;
					boolean hasnext=false;
					for(int i=0;i<customerList.size();i++){
						customer=customerList.get(i);//获取下一个用户信息
						if(windows.getWindows_Business().contains(customer.getCustomer_Business_ID())) {
							hasnext=true;
							break;
						}
						else {
						}
					}
					
					if(hasnext) {
						start_time=Total_time+Open_time;
						if(customer.isIf_VIP()){//VIP用户拥有两条队伍
							customerList.remove(customer);
							VIP_customerList.remove(customer);
						}else {//普通用户只有一条队伍
							customerList.remove(customer);
						}
						lock.unlock();
						
						business_id=customer.getCustomer_Business_ID();
						business_time=Bank_Random.random_time(business_enum_map.get(business_id).getBusiness_MinTime()
								, business_enum_map.get(business_id).getBusiness_MaxTime());//生成随机业务时间
						business_statistic_map.put(business_id, business_statistic_map.get(business_id)+1);
						customer.setBusiness_Time(business_time);
						
						System.out.println("【叫号】\t"+dft.format(start_time)+"\t请"+customer.getCustomer_ID()
							+"号客户到"+windows.getWindows_Name()+"窗口受理"+customer.getCustomer_Business_ID()+"号业务\t"
							+ "预计时间"+double2.format(1.0*customer.getBusiness_Time()/60000)+"分");
						
						try {
							Thread.sleep(1);
						} catch (Exception e) {
							e.printStackTrace();
						}
			
						customer.setFinish_Time(start_time+business_time);
						windows.setBusiness_end_time(start_time+business_time);
						Served_time+=business_time;//加入累计总时间
						serve_B_time+=business_time;//加入累计A时间
						Waited_time+=(customer.getFinish_Time()-customer.getArrive_Time());//加入累计总时间
						wait_B_time+=(customer.getFinish_Time()-customer.getArrive_Time());//加入累计B时间
						
						//记录数据
						Bank_Business_Record record=new Bank_Business_Record();
						record.setArrive_time(customer.getArrive_Time());
						record.setFinish_time(customer.getFinish_Time());
						record.setIndex(customer.getCustomer_Index());
						record.setBussiness_id(business_id);
						record.setBusiness_name(business_enum_map.get(business_id).getBusiness_Name());
						record.setWindows_ID(windows.getWindows_ID());
						record.setWindows_name(windows.getWindows_Name());
						record.setWindows_type(windows.getWindows_Type());
						record.setCustomer_ID(customer.getCustomer_ID());
						record.setIf_VIP(customer.isIf_VIP());
						record.setBusiness_time(customer.getBusiness_Time());
						recordList.add(record);
						recordListB.add(record);
						if(!customer.isIf_VIP()) {
							served_normal++;
						}else {
							served_VIP++;
						}
						
						System.out.println("【完成】\t"+customer.getCustomer_ID()+"号客户将于"+dft.format(customer.getFinish_Time())
							+"在"+windows.getWindows_Name()+"窗口完成“"+record.business_name+"”业务");	
						Close_time=customer.getFinish_Time();
						timerun=true;
					}
					else {
						lock.unlock();
						//System.out.println("【空闲】\t"+dft.format(Open_time+Total_time)+"\t"+windows.getWindows_Name()+"窗口无适配服务\t等待客户中");
						try {
							Thread.sleep(1);
						} catch (Exception e) {
							e.printStackTrace();
						}
						timerun=true;
						continue;
					}
					
				} else {
					//System.out.println("【空闲】\t"+dft.format(Open_time+Total_time)+"\t"+windows.getWindows_Name()+"窗口空闲\t等待客户中");
					lock.unlock();
					try {
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					timerun=true;
					continue;
				}
			
			}
		}

(17)C窗口办理业务方法
public static class Windows_ServerC implements Runnable{

		public Bank_Windows windows;
		public Windows_ServerC(Bank_Windows windows) {
			this.windows=windows;
		}
		@Override
		public void run() {
			while(true) {
				
				if(Total_time>=32400000l && customerList.isEmpty()) {
					break;
				}
				else if((Total_time+Open_time)<windows.getBusiness_end_time()) {
					try {		
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					continue;
				}
				lock.lock();
				timerun=false;
				if(!VIP_customerList.isEmpty()||!customerList.isEmpty()) {
					Bank_Customer customer =new Bank_Customer();
					long business_time;
					long start_time = 0l;
					int business_id;
					start_time=Total_time+Open_time;
					if(!VIP_customerList.isEmpty()) {
						customer=VIP_customerList.get(0);
						customerList.remove(customer);
						VIP_customerList.remove(customer);
						lock.unlock();
					}
					else if(!customerList.isEmpty()) {
						customer=customerList.get(0);//获取下一个用户信息
						customerList.remove(customer);
						lock.unlock();
					}
					
					
					business_id=customer.getCustomer_Business_ID();
					business_time=Bank_Random.random_time(business_enum_map.get(business_id).getBusiness_MinTime()
							, business_enum_map.get(business_id).getBusiness_MaxTime());//生成随机业务时间
					business_statistic_map.put(business_id, business_statistic_map.get(business_id)+1);
					customer.setBusiness_Time(business_time);
					
					System.out.println("【叫号】\t"+dft.format(start_time)+"\t请"+customer.getCustomer_ID()+"号客户到"+windows.getWindows_Name()+"窗口受理"+customer.getCustomer_Business_ID()+"号业务\t"+ "预计时间"+double2.format(1.0*customer.getBusiness_Time()/60000)+"分");
				
					try {
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					
					customer.setFinish_Time(start_time+business_time);
					windows.setBusiness_end_time(start_time+business_time);
					Served_time+=business_time;//加入累计总时间
					serve_C_time+=business_time;//加入累计A时间
					Waited_time+=(customer.getFinish_Time()-customer.getArrive_Time());//加入累计总时间
					wait_C_time+=(customer.getFinish_Time()-customer.getArrive_Time());//加入累计C时间
					
					//记录数据
					Bank_Business_Record record=new Bank_Business_Record();
					record.setArrive_time(customer.getArrive_Time());
					record.setFinish_time(customer.getFinish_Time());
					record.setIndex(customer.getCustomer_Index());
					record.setBussiness_id(business_id);
					record.setBusiness_name(business_enum_map.get(business_id).getBusiness_Name());
					record.setWindows_ID(windows.getWindows_ID());
					record.setWindows_name(windows.getWindows_Name());
					record.setWindows_type(windows.getWindows_Type());
					record.setCustomer_ID(customer.getCustomer_ID());
					record.setIf_VIP(customer.isIf_VIP());
					record.setBusiness_time(customer.getBusiness_Time());
					recordList.add(record);
					recordListC.add(record);
					if(!customer.isIf_VIP()) {
						served_normal++;
					}else {
						served_VIP++;
					}
					
					System.out.println("【完成】\t"+customer.getCustomer_ID()+"号客户将于"+dft.format(customer.getFinish_Time())+"在"+windows.getWindows_Name()+"窗口完成“"+record.business_name+"”业务");	
					Close_time=customer.getFinish_Time();
					timerun=true;
					
				} else {
					//System.out.println("【空闲】\t"+dft.format(Open_time+Total_time)+"\t"+windows.getWindows_Name()+"窗口空闲\t等待客户中");
					lock.unlock();
					try {
						Thread.sleep(1);
					} catch (Exception e) {
						e.printStackTrace();
					}
					timerun=true;
					continue;
				}
			}
		}

7. 测试用例

  • 线性随机分配函数证明:

(1)设计方法证明随机分配的有效性与可靠性
(2)Java中的Math.random()是线性均匀的
(3)根据自定义的数据范围对生成数的百分比进行控制
(4)下图证明了方法可以控制随机数生成的比例可以固定在“3:7”,且保持了随机性

  • 开业操作:

  • 随机生成造成拥堵,进行排队:

(1)VIP队列:根据VIP客户到达时间进行取号排序。
(2)普通队列:根据普通客户、VIP客户到达时间进行取号排序。

  • 结束营业操作,包含:

(1)结束营业时间(客户生成方法在17:00前随机生成,排队造成的业务延期将会延长上班时间,延后结束营业时间)
(2)今日服务人数
(3)VIP与普通客户的占比
(4)各业务办理的占比
(5)累计业务服务时间、各类窗口业务服务时间占比、人均业务服务时间
(5)累计等待时间、各类窗口等待时间占比、人均等待时间

  • 建议窗口数量,分析方面:

(1)根据VIP占比
(2)根据B窗口不支持业务
(3)根据网银业务占比(网银业务时间占比大)
(4)根据客户平均等待时间
(5)以下是多情况运行截图

  • 客户列表:

(1)总客户列表,无序,按照排到的业务顺序
(2)A类窗口客户列表,有序,按照取号码升序
(3)B类窗口客户列表,有序,按照取号码升序
(4)C类窗口客户列表,有序,按照取号码升序


8. 总结

  • 学习:

(1)新接触了Enum类,学习并掌握其使用方法
(2)第一次尝试使用时间流动方法,根据秒来进行操作
(3)使用Math.Random来进行固定比例的生成
(4)尝试了新的方法来加锁与解锁

  • 巩固:

(1)巩固了时间的计算与调用
(2)多次使用HashMap与ArrayList,并调用方法
(3)类的创建、定义与调用的巩固
(4)结合了OS系统原理内容,分析银行叫号、分配业务、办理的同步与互斥

  • 缺漏:

(1)时间的定义还需完善
(2)互斥的部分调试时间较长
(3)分析窗口不够细,需要完善
(4)打印格式、代码模块比较乱

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是您的第二个问题。下面是一个简单的多线程模拟银行叫号系统的示例代码: 首先,您需要创建一个Ticket类,用于表示每个顾客的票号和窗口号,如下所示: ```java public class Ticket { private int number; private int window; public Ticket(int number, int window) { this.number = number; this.window = window; } public int getNumber() { return number; } public int getWindow() { return window; } } ``` 然后,您可以创建一个银行类Bank,用于模拟叫号系统,如下所示: ```java import java.util.LinkedList; import java.util.Queue; public class Bank { private int windowCount; // 窗口数量 private Queue<Integer> ticketQueue; // 顾客票号队列 private Ticket[] windows; // 窗口顾客票号数组 public Bank(int windowCount) { this.windowCount = windowCount; ticketQueue = new LinkedList<Integer>(); windows = new Ticket[windowCount]; } public synchronized void takeTicket() { int ticketNumber = ticketQueue.size() + 1; ticketQueue.offer(ticketNumber); System.out.println("顾客" + ticketNumber + "取号成功,当前排队人数:" + ticketQueue.size()); notifyAll(); } public synchronized void callTicket(int windowNumber) { while (true) { if (ticketQueue.isEmpty()) { System.out.println("窗口" + windowNumber + "等待顾客中..."); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { int ticketNumber = ticketQueue.poll(); Ticket ticket = new Ticket(ticketNumber, windowNumber); windows[windowNumber - 1] = ticket; System.out.println("窗口" + windowNumber + "叫到了顾客" + ticketNumber + ",请前往" + windowNumber + "号窗口办理业务"); notifyAll(); break; } } } public synchronized void finishTicket(int windowNumber) { Ticket ticket = windows[windowNumber - 1]; if (ticket != null) { System.out.println("窗口" + windowNumber + "完成了顾客" + ticket.getNumber() + "的业务"); windows[windowNumber - 1] = null; notifyAll(); } } } ``` Bank类的构造函数接收一个窗口数量,初始化一个顾客票号队列和一个窗口顾客票号数组。takeTicket()方法用于顾客取号,它首先为当前顾客生成一个顾客票号,然后将其加入到队列中,并输出当前排队人数。callTicket()方法用于窗口叫号,它首先检查队列是否为空,如果为空,则窗口等待;否则,窗口叫到了下一个顾客,并将其从队列中弹出,并将其票号和窗口号存储到窗口顾客票号数组中,并输出调用信息。finishTicket()方法用于窗口完成业务,它首先检查窗口是否有顾客,如果没有,则等待;否则,窗口完成了当前顾客的业务,并将窗口顾客票号数组中的该位置清空,并输出调用信息。 最后,您可以创建一个模拟程序,用于创建多个顾客线程和多个窗口线程,来模拟银行叫号系统,如下所示: ```java public class BankSimulation { public static void main(String[] args) { Bank bank = new Bank(3); // 创建多个顾客线程 for (int i = 1; i <= 10; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { bank.takeTicket(); } }); thread.start(); } // 创建多个窗口线程 for (int i = 1; i <= 3; i++) { int windowNumber = i; Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { bank.callTicket(windowNumber); try { Thread.sleep(2000); // 模拟窗口处理业务的时间 } catch (InterruptedException e) { e.printStackTrace(); } bank.finishTicket(windowNumber); } } }); thread.start(); } } } ``` 上述代码将创建10个顾客线程和3个窗口线程,模拟银行叫号系统的运行过程。您可以根据自己的需要对其进行修改。希望能够帮到您,如果您有任何问题,请随时问我!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值