多線程銀行業務辦理模擬——2020高級程序設計_期中


ZUCC BK阿碼農 2020年11月17日

1. 項目介紹

設計一個多線程模擬銀行業務辦理程序:

  • 業務時間

  模擬客戶可以隨機辦理銀行提供的8種業務中的一種(基准時間自定義),辦理時間規划為:

業務 業務序號 辦理時間范圍
取款 1 0.5-1.5r
存款 2 0.5-1.5r
繳納罰款 3 1.2-2.0r
開通網銀 4 5.0-8.0r
繳納水電費 5 1.5-2.0r
購買基金 6 2.0-3.0r
轉賬匯款 7 3.0-4.0r
個貸還款 8 2.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,8 VIP優先(所有客戶)

  模擬開放為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. 仿真性比例自定義

  • 業務比例定義

業務 業務序號 自定義
取款 1 20%
存款 2 15%
繳納罰款 3 10%
開通網銀 4 10%
繳納水電費 5 10%
購買基金 6 10%
轉賬匯款 7 15%
個貸還款 8 10%
  • 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)打印格式、代碼模塊比較亂


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM