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

本文档介绍了一个多线程程序设计,用于模拟银行业务办理。建议使用线程池来优化代码。项目包括业务时间、服务窗口、模拟客户、客户列表展示、数据统计和仿真性要求等模块。核心类包括Bank、Utils、BankExecutor等,并提供了测试用例以验证程序的正确性。
摘要由CSDN通过智能技术生成

多线程模拟银行业务办理

PS.强烈建议使用线程池来做,个人估计可以比下面的代码少一半。

项目介绍

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

(1)业务时间

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

业务 业务序号 办理时间范围
取款 1 0.5* 基准时间-1.5* 基准时间
存款 2 0.5* 基准时间-1.5* 基准时间
缴纳罚款 3 1.2* 基准时间-2.0* 基准时间
开通网银 4 5.0* 基准时间-8.0* 基准时间
缴纳水电费 5 1.5* 基准时间-2.0* 基准时间
购买基金 6 2.0* 基准时间-3.0* 基准时间
转账汇款 7 3.0* 基准时间-4.0* 基准时间
个贷还款 8 2.0* 基准时间-4.0* 基准时间

(2)服务窗口

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

业务 业务序号 办理时间范围
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优先

(3)模拟客户

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

(4)展示客户列表

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

(5)统计数据

当天客户平均业务办理时间
  当天客户所有办理业务中的所占比例

(6)仿真性要求

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

考核要求

  • 基本要求:

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

  • 设计核心问题:

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

  • 项目上交:

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


主要功能

  • 实现营业过程(开门、关门、营业分配)

  • 实现随机生成用户及需办理业务

  • 实现合理窗口分配

  • 实现多线程、线程同步、线程互斥

  • 实现计算当天办理平均时间、当天业务占比

  • 实现参数化分配业务

  • 实现计算窗口开放建议


类职责划分

UML图

描述

  • Bank,负责执行题目要求的计算逻辑。
  • Utils,一些工具函数,例如取随机数或者打印数据。
  • DateGenerator,随机数据的生成类。
  • BankExecutor,核心代码,业务模拟执行、调度的核心。
  • ServiceWindow:BankExecutor的内部类,表示服务窗口。
  • User:用户类 。
  • BusinessEnum:业务类型枚举类。
  • ServiceWindowEnum: 服务窗口枚举类。

核心代码

bank.java



package com.yzl;


/**
 * 银行
 *
 */
public class Bank {
   
    public static final int TOTAL_WINDOWS = 4;

    public static void main(String[] args) throws InterruptedException {
   
        // 测试基准日
        double res = doBasicDayTest();
        System.out.println("基准日平均办理时间:" + res + "s");

        // 寻找和基准日最相近的窗口
        getBestWindowCondition();
    }

    /**
     * 传入窗口总数,模拟当日活动
     *
     * @param a        a窗口数目
     * @param b        b窗口数目
     * @param v        v窗口数目
     * @param business 业务比例(1个或者多个)
     */
    public static Double doDayTest(Integer a, Integer b, Integer v, Double... business) throws InterruptedException {
   
        BankExecutor bankExecutor = new BankExecutor();
        System.out.printf("%-15s %-15s %-15s %-15s %-15s %-15s\n", "窗口", "用户", "是否VIP", "业务", "本次服务时间", "用户到达时间");
        DataGenerator dataGenerator = new DataGenerator(bankExecutor);
        dataGenerator.generateUsers(business);
        dataGenerator.generateSampleWindow(a, b, v);
        // 等待银行“关门”
        Thread.sleep(1000);
        // 设置关门状态
        bankExecutor.shutDownBank();
        // 执行到此,所有窗口都已完成关闭(shutDownBank会阻塞),我们遍历所有窗口来查看状态
        return Utils.showWindowCondition(bankExecutor);
    }

    /**
     * 基准日
     */
    public static Double doBasicDayTest() throws InterruptedException {
   
        return doDayTest(1, 2, 1, 0.2, 0.2, 0.1, 0.1, 0.05, 0.15, 0.10, 0.10);
    }

    /**
     * 获取最佳窗口开放情况
     */
    public static void getBestWindowCondition() throws InterruptedException {
   
        // 基准日
        double res = doBasicDayTest();
        double minTimeDelta = Integer.MAX_VALUE;
        double minTime = Integer.MAX_VALUE;
        int oa = 1;
        int ob = 2;
        int ov = 1;
        // 排列组合,我们的总窗口数不变
        for (int i = 0; i <= TOTAL_WINDOWS; i++) {
   
            for (int j = 0; j <= TOTAL_WINDOWS - i; j++) {
   
                int k = TOTAL_WINDOWS - i - j;
                // 排除基准日
                if (i == 1 && j == 2 && k == 1) {
   
                    continue;
                }
                System.out.println("测试:" + i + "A " + j + "B " + k + "V");
                double tmp = doDayTest(i, j, k, 0.1, 0.1, 0.05, 0.05, 0.05, 0.4, 0.05, 0.2);
                if (Math.abs(tmp - res) < minTimeDelta) {
   
                    minTimeDelta = Math.abs(tmp - res);
                    minTime = tmp;
                    oa = i;
                    ob = j;
                    ov = k;
                }
            }
        }
        System.out.println("和基准日基本一致的窗口开发情况:");
        System.out.println("A窗口" + oa);
        System.out.println("B窗口" + ob);
        System.out.println("V窗口" + ov);
        System.out.println
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,这是您的第二个问题。下面是一个简单的多线程模拟银行叫号系统的示例代码: 首先,您需要创建一个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个窗口线程,模拟银行叫号系统的运行过程。您可以根据自己的需要对其进行修改。希望能够帮到您,如果您有任何问题,请随时问我!
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值