[BUAA OO Unit 2]单元总结

2306不只有coding 2306不只有coding 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区

目录

一、前言

二、同步块的设置和锁的选择

三、调度器设计与线程交互

1、第五次作业

2、第六次作业

3、第七次作业

四、结合线程协同的架构模式

1、第五次作业

(1)、UML类图

 (2)、UML协作图

2、第六次作业

(1)、UML类图

(2)、UML协作图

3、第七次作业

(1)、UML类图

(2)、UML协作图 

五、双轿厢改造与电梯碰撞

六、bug发现与修复

七、心得体会

1、线程安全

2、层次化设计


一、前言

本文将以笔者在面向对象第二单元的三次作业中的思考感悟为基础,对2025年OO第二单元的代码的架构搭建与程序设计思路做简单的分享,欢迎指出问题与交流思路。

二、同步块的设置和锁的选择

本单元三次作业的的锁均采用由synchronized修饰的的同步方法

对项目中的共享对象的所有方法添加synchronized修饰,保证了共享对象不会产生读写冲突以及保证了双轿厢电梯升级过程中和升级后的输出冲突问题。

锁与同步块中处理语句之间的关系

当一个线程开始进行共享对象的同步方法时,其他线程对于该共享对象的同步方法调用被阻塞,即同一时间共享对象的同步方法只有一个会在运行中,避免共享对象的读写冲突。同时,可以在同步方法中加入wait()-notify()机制以避免线程的轮询。

三、调度器设计与线程交互

调度策略

在三次作业中,电梯的纵向运行策略均为ALS策略,电梯的横向分配策略均为平均分配策略(以适应时间、电量等多个性能指标)。线程交互基本是基于两个生产消费者模型。


1、第五次作业

输入线程

与调度器线程共享一个请求队列,输入线程不断接受输入,如果没有输入就阻塞等待;如果有输入就将输入添加到请求队列中;输入结束就通知设置请求队列输入结束,并自身退出循环结束线程。

调度器线程

与输入线程共享一个请求队列,与电梯线程共享一个候乘表。调度器线程不断从请求队列中获取请求,如果获取到了请求,就根据请求的电梯序号将请求添加到对应的电梯的候乘表。当调度器线程与输入线程共享的请求队列被输入线程标记为输入结束,且请求队列为空时,将六个电梯线程的候乘表设置为工作结束,并退出循环,结束线程。

电梯线程

 

与调度器线程共享一个候乘表,并拥有乘客队列。在电梯的run方法循环中:

1.首先判断候乘表和乘客队列是否为空,若候乘表和乘客队列为空再判断是否候乘表工作结束,若候乘表工作结束则退出循环结束线程,否则电梯wait等待乘客。

2.接着根据策略类的判断设置电梯运行方向

3.再判断是否有乘客可以进入和是否有乘客需要离开,若有则开门并进行乘客离开和进入的操作(应当先离开再进入,否则可能出现拒载的情况)。

4.在电梯开门关门后再进行一次乘客队列为空的判断(可能出现乘客已经全部离开的情况,若不判断,则电梯会以旧的方向到新的一层)

5.根据策略类给出的方向进行电梯移动。


2、第六次作业

输入线程

相比上一次作业新增了临时调度请求,其他不变。

调度器线程

新增了临时调度请求的分配,在线程结束的条件判断以及getRequest()方法的进入wait()的条件判断上加入与临时调度请求的相关内容。

电梯线程

在电梯的run方法循环中:

新增了requestQueue为电梯线程与调度器线程的共享对象以实现

1.首先判断候乘表的乘客请求与临时调度请求和乘客队列是否为空,若候乘表和乘客队列为空再判断是否候乘表工作结束,若候乘表工作结束则退出循环结束线程,否则电梯wait等待乘客。

2.判断候乘表里是否有临时调度请求(若有则进入临时调度的处理方法,若无则向下进行)

2.接着根据策略类的判断设置电梯运行方向

3.再判断是否有乘客可以进入和是否有乘客需要离开,若有则开门并进行乘客离开和进入的操作(应当先离开再进入,否则可能出现拒载的情况)。

4.在电梯开门关门后再进行一次乘客队列为空的判断(可能出现乘客已经全部离开的情况,若不判断,则电梯会以旧的方向到新的一层)

5.根据策略类给出的方向进行电梯移动。


3、第七次作业

输入线程

较上一次作业增加了双轿厢电梯升级请求,其他不变。

调度器线程

新增了双轿厢电梯升级请求的分配,在分配过程中实现满足双轿厢电梯的楼层限制的的分配逻辑,并将不能一次性到达目标楼层的乘客请求记为一个集合,该集合参与调度器线程的结束判断以及getRequest()方法的wait()条件判断。

电梯线程

较上次作业新加入双轿厢电梯升级请求的处理过程,加入Elevatorshaft作为双轿厢两个电梯的共享对象,其升级处理以及处理后的电梯移动均在该类中实现(利用synchronized修饰的同步方法来避免电梯碰撞以及升级过程中的输出与电梯运行逻辑的正确进行)。

四、结合线程协同的架构模式


1、第五次作业

(1)、UML类图

 

 (2)、UML协作图


 

 

2、第六次作业

(1)、UML类图

 

(2)、UML协作图


 

 

3、第七次作业

(1)、UML类图

 

(2)、UML协作图 

 

 稳定与易变

MainClass与InputHandler基本稳定,而Scheduler、Elevator、RequestQueue易变(由于新增请求类型与新增请求处理方式以及不同的调度实现)。

五、双轿厢改造与电梯碰撞

我通过构建ElevatorShaft类,将其作为改造的两个电梯的共享对象,并在其中实现upRequestDoneA()、upRequestDoneB()、moveA()、moveB()同步方法。

在upRequestDoneA()、upRequestDoneB()中通过wait()-notifyAll()的机制实现双轿厢的同步开始改造以及电梯运行逻辑与改造请求输出的相适配。

public synchronized void upRequestDoneA(
            UpdateRequest updateRequest, LinkedList<PeopleRequest> waiting) {
        elevatorA.setDirection(0);
        elevatorA.setSpeed(0.2);
        LinkedList<PeopleRequest> outF = new LinkedList<>();
        if (!elevatorA.getPassengers().isEmpty()) {
            elevatorA.openDoor();
            for (PeopleRequest peopleRequest : elevatorA.getPassengers()) {
                elevatorA.passengerOut(peopleRequest);
                if (!peopleRequest.getToFloor().equals(elevatorA.getCurrentFloor())) {
                    peopleRequest.setFromFloor(elevatorA.getCurrentFloor());
                    outF.add(peopleRequest);
                    //System.out.println(peopleRequest.getPersonId() + "B");
                }
            }
            elevatorA.getPassengers().clear();
            elevatorA.closeDoor();
        }
        opA = 1;
        notifyAll();
        if (opB == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        for (PeopleRequest peopleRequest : outF) {
            requestQueue.addRequest(peopleRequest);
        }
        if (!waiting.isEmpty()) {
            for (PeopleRequest peopleRequest : waiting) {
                requestQueue.addFRequest(peopleRequest);
                //System.out.println(peopleRequest.getPersonId() + "B");
            }
        }
        elevatorA.setCurrentFloor(elevatorA.getWaitingTable().changeNum(
                elevatorA.getWaitingTable().changeFu(updateRequest.getTransferFloor()) + 1));
        elevatorA.setLowFloor(updateRequest.getTransferFloor());
        elevatorA.setOp(1);
        elevatorA.setIdu(elevatorB.getId());
        elevatorA.getWaitingTable().removeU();
    }

    public synchronized void upRequestDoneB(
            UpdateRequest updateRequest, LinkedList<PeopleRequest> waiting) {
        elevatorB.setDirection(0);
        elevatorB.setSpeed(0.2);
        LinkedList<PeopleRequest> outF = new LinkedList<>();
        if (!elevatorB.getPassengers().isEmpty()) {
            elevatorB.openDoor();
            for (PeopleRequest peopleRequest : elevatorB.getPassengers()) {
                elevatorB.passengerOut(peopleRequest);
                if (!peopleRequest.getToFloor().equals(elevatorB.getCurrentFloor())) {
                    peopleRequest.setFromFloor(elevatorB.getCurrentFloor());
                    outF.add(peopleRequest);
                    //System.out.println(peopleRequest.getPersonId() + "B");
                }
            }
            elevatorB.getPassengers().clear();
            elevatorB.closeDoor();
        }
        if (opA == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        TimableOutput.println("UPDATE-BEGIN-" +
                updateRequest.getElevatorAId() + "-" + updateRequest.getElevatorBId());
        for (PeopleRequest peopleRequest : outF) {
            requestQueue.addRequest(peopleRequest);
        }
        if (!waiting.isEmpty()) {
            for (PeopleRequest peopleRequest : waiting) {
                requestQueue.addFRequest(peopleRequest);
                //System.out.println(peopleRequest.getPersonId() + "B");
            }
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        elevatorB.setCurrentFloor(elevatorB.getWaitingTable().changeNum(
                elevatorB.getWaitingTable().changeFu(updateRequest.getTransferFloor()) - 1));
        TimableOutput.println("UPDATE-END-" +
                updateRequest.getElevatorAId() + "-" + updateRequest.getElevatorBId());
        opB = 1;
        notifyAll();
        elevatorB.setHighFloor(updateRequest.getTransferFloor());
        elevatorB.setOp(-1);
        elevatorB.setIdu(elevatorA.getId());
        elevatorB.getWaitingTable().removeU();
    }

用moveA()、moveB()代替之前的电梯移动函数已实现同一时间只有一个move()方法在运行,来避免电梯碰撞问题。

public synchronized void moveA() {
        int time = (int) (elevatorA.getSpeed() * 1000);
        if (elevatorA.getDirection() == 1 &&
                !elevatorA.getCurrentFloor().equals(elevatorA.getHighFloor())) {
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            elevatorA.setCurrentFloor(elevatorA.getWaitingTable().changeNum(
                    elevatorA.getWaitingTable().changeFu(elevatorA.getCurrentFloor()) + 1));
            elevatorA.arrive();
        } else if (elevatorA.getDirection() == -1 &&
                !elevatorA.getCurrentFloor().equals(elevatorA.getLowFloor())) {
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            elevatorA.setCurrentFloor(elevatorA.getWaitingTable().changeNum(
                    elevatorA.getWaitingTable().changeFu(elevatorA.getCurrentFloor()) - 1));
            elevatorA.arrive();
        }
        if (elevatorA.getCurrentFloor().equals(elevatorA.getLowFloor())) {
            if (!elevatorA.getPassengers().isEmpty() || elevatorA.getWaitingTable().hasFloorRequest(
                    elevatorA.getOp(),elevatorA.getPassengers().size(),
                    elevatorA.getCurrentFloor())) {
                elevatorA.openDoor();
                for (PeopleRequest peopleRequest : elevatorA.getPassengers()) {
                    elevatorA.passengerOut(peopleRequest);
                    if (!peopleRequest.getToFloor().equals(elevatorA.getCurrentFloor())) {
                        peopleRequest.setFromFloor(elevatorA.getCurrentFloor());
                        requestQueue.addFRequest(peopleRequest);
                        //System.out.println(peopleRequest.getPersonId() + "B");
                    }
                }
                elevatorA.getPassengers().clear();
                elevatorA.getWaitingTable().pickUp(elevatorA.getOp(),
                        elevatorA.getCurrentFloor(), elevatorA.getPassengers(),elevatorA.getId());
                elevatorA.closeDoor();
            }
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            elevatorA.setCurrentFloor(elevatorA.getWaitingTable().changeNum(
                    elevatorA.getWaitingTable().changeFu(elevatorA.getCurrentFloor()) + 1));
            elevatorA.arrive();
        }
    }

    public synchronized void moveB() {
        int time = (int) (elevatorB.getSpeed() * 1000);
        if (elevatorB.getDirection() == 1 &&
                !elevatorB.getCurrentFloor().equals(elevatorB.getHighFloor())) {
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            elevatorB.setCurrentFloor(elevatorB.getWaitingTable().changeNum(
                    elevatorB.getWaitingTable().changeFu(elevatorB.getCurrentFloor()) + 1));
            elevatorB.arrive();
        } else if (elevatorB.getDirection() == -1 &&
                !elevatorB.getCurrentFloor().equals(elevatorB.getLowFloor())) {
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            elevatorB.setCurrentFloor(elevatorB.getWaitingTable().changeNum(
                    elevatorB.getWaitingTable().changeFu(elevatorB.getCurrentFloor()) - 1));
            elevatorB.arrive();
        }
        if (elevatorB.getCurrentFloor().equals(elevatorB.getHighFloor())) {
            if (!elevatorB.getPassengers().isEmpty() || elevatorB.getWaitingTable().hasFloorRequest(
                    elevatorB.getOp(),elevatorB.getPassengers().size(),
                    elevatorB.getCurrentFloor())) {
                elevatorB.openDoor();
                for (PeopleRequest peopleRequest : elevatorB.getPassengers()) {
                    elevatorB.passengerOut(peopleRequest);
                    if (!peopleRequest.getToFloor().equals(elevatorB.getCurrentFloor())) {
                        peopleRequest.setFromFloor(elevatorB.getCurrentFloor());
                        requestQueue.addFRequest(peopleRequest);
                        //System.out.println(peopleRequest.getPersonId() + "B");
                    }
                }
                elevatorB.getPassengers().clear();
                elevatorB.getWaitingTable().pickUp(elevatorB.getOp(),
                        elevatorB.getCurrentFloor(), elevatorB.getPassengers(),elevatorB.getId());
                elevatorB.closeDoor();
            }
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            elevatorB.setCurrentFloor(elevatorB.getWaitingTable().changeNum(
                    elevatorB.getWaitingTable().changeFu(elevatorB.getCurrentFloor()) - 1));
            elevatorB.arrive();
        }
    }

六、bug发现与修复

第五次作业

Bug为由于电梯上下完乘客后,电梯中没有乘客时,由于缺少该判断而产生的电梯在旧方向上多走一层的问题,加上相关判断后continue即可。

第六次作业

Bug为由于receive输出位置有误而导致的Wrong_State问题以及由于getRequest()中wait()的条件判断中缺少临时调度请求为空的情况而产生的Time error。

将receive的输出位置添加至pickup方法中以解决问题一,将wait()的条件判断加上临时调度请求为空的情况以解决问题二。

第七次作业

Bug为由于getRequest()中wait()条件判断的不完整而导致的wait()提前失效而产生的Scheduler轮询问题。

修改条件从isEmpty() && !isFinished && SisEmpty() && UisEmpty()为isEmpty() && SisEmpty() && UisEmpty() && (!isFinished || !FisEmpty())以解决该问题。

七、心得体会

1、线程安全

要在合适的地方加锁,不要滥用加锁,确定好哪段代码是临界区(尽可能小,提高并发效率),将线程安全类与普通类分离开来。也不要滥用notifyall(可能导致轮询)。缕清各线程之间的时序问题并利用同步方法保证线程安全。

2、层次化设计

采用了简单了生产者消费者模型和单例模式。

整体的框架在第一次作业时已经建立,后续的作业只是在第一次作业的基础上迭代开发,一路沿用上次作业的架构,导致类的耦合度有点高,并且是面向具体编程,也没有面向接口编程,希望下个单元能够改进。

...全文

相关推荐

弱电网下光伏并网逆变器序阻抗建模仿真复现扫频法【序阻抗判断稳定性】

内容概要:本文聚焦于弱电网条件下光伏并网逆变器的序阻抗建模与稳定性分析,系统阐述了基于扫频法的阻抗特性仿真与复现方法。通过构建正负序阻抗模型,深入剖析逆变器在弱电网环境中的动态响应特性,结合锁相环、电流环等关键控制环节对系统阻抗的影响机制,采用序阻抗稳定性判据评估多机并网或复杂电网条件下的系统稳定裕度。研究在Simulink平台中实现激励信号注入、频率响应采集与阻抗曲线绘制,为高比例新能源接入背景下并网系统的稳定性分析提供了可靠的仿真手段与理论支撑,尤其适用于SCI级别论文成果的复现与拓展验证。; 适合人群:电力电子、新能源并网技术及相关领域的硕士/博士研究生、科研人员,以及从事光伏逆变器、微电网稳定控制开发的工程技术人员。; 使用场景及目标:①掌握扫频法在电力电子装置阻抗建模中的具体实施流程;②理解并应用基于序阻抗的稳定性判据进行系统稳定边界分析;③用于高水平学术论文(如SCI期刊)中阻抗建模与稳定性分析部分的仿真复现与方法验证; 阅读建议:建议读者结合文中所述方法与提供的参考文献,自主搭建Simulink仿真模型,逐步完成扰动信号设计、数据采集、傅里叶分析及阻抗拟合全过程,深入探究控制参数变化对系统阻抗特性和稳定性的具体影响,强化理论与实践的融合能力。

欢多天天写代码

三相并网逆变器虚拟阻抗+统一有源阻尼策略SVPWM+SPWM调制仿真

内容概要:本文档系统性地介绍了三相并网逆变器的核心控制技术及其在复杂电网环境下的仿真应用,重点涵盖虚拟阻抗与统一有源阻尼策略在SVPWM和SPWM调制方式下的实现方法,旨在提升逆变器的稳定性与电能质量。内容包括无功功率扰动孤岛检测、中点电位平衡控制、低电压穿越(LVRT)、模型预测控制(MPC)、正负序分离、电网前馈控制等关键技术,并扩展至风储联合调频、微电网下垂控制、虚拟同步发电机(VSG)、构网型逆变器等多种典型电力系统应用场景。所有技术均配有基于Simulink/Matlab平台的完整仿真模型、参考文献及说明文档,强调理论分析与仿真实践的深度融合,突出科研中“借力”与创新思维的重要性。; 适合人群:电气工程、自动化、新能源等相关专业的研究生、科研人员及从事电力电子与可再生能源系统开发的工程师;具备MATLAB/Simulink仿真基础和电力系统专业知识的技术人员。; 使用场景及目标:①开展三相并网逆变器控制策略的研究与优化设计;②复现高水平学术论文中的控制算法;③完成毕业课题、科研项目或工程仿真验证;④掌握虚拟阻抗、有源阻尼、SVPWM/SPWM调制、MPC、VSG等核心技术的建模与实现方法;⑤支撑微电网、构网型变流器等前沿方向的科研攻关。; 阅读建议:建议按照资源目录结构化学习,优先选择与自身研究方向契合的内容模块,结合提供的仿真模型、参考文献和说明材料进行实操演练,注重从理论推导到仿真实现的全过程理解,充分利用共享网盘资源深化技术掌握,提升科研效率与创新能力。

欢多天天写代码

chromedriver-win32-159.0.8133.0(Canary).zip

chromedriver-win32-159.0.8133.0(Canary).zip

LCG元

计算机“101计划”《人工智能引论》教学大纲+课件+实训案例

计算机“101计划”《人工智能引论》教学大纲+课件+实训案例

qq_38220914

语音输入法离线语音输入转文字工具2.0支持双档本地识别模型,中英混说精准转写,纯离线运行保护隐私,让打字更高效.zip

大家好,我是大飞哥。日常打字时,频繁切换输入法、断网无法语音转写、或担心语音数据被传至云端,是许多办公族和内容创作者的共同痛点。这款离线语音输入工具 ,正是为此量身打造的 Windows 桌面语音辅助利器。它的核心使命就是按住快捷键说话,松开文字自动上屏,全程本地离线识别,让您在任何可输入文字的窗口内都能实现高效、私密的语音输入,彻底告别网络依赖与隐私焦虑。 在核心功能上,软件内置了两档本地语音识别模型,通过主界面下拉框即可随时切换。默认的“离线·标准”模式响应极快,支持中英混说且自带标点,非常适合日常聊天与文档撰写;而“离线·精准”模式则对纯中文、方言口音及长段落口述的识别准确度更高。更贴心的是,它具备实时预览与一键取消机制,说话过程中若发现说错,按下 Esc 即可取消,一个字都不会误输入;若目标窗口不支持直接写入,文字会自动存入剪贴板,方便您按 Ctrl+V 粘贴,确保每次发声都不白费。 这款工具在具体场景中的表现十分亮眼。对于办公白领,在填写表格、撰写邮件或使用微信、QQ 沟通时,无需频繁切换窗口,按住右键说话即可快速完成信息录入;对于程序员与文字工作者,在代码编辑器或文档中口述注释与段落,能显著提升创作效率;对于经常出差的商务人士,在飞机、高铁等断网环境下,它依然能稳定工作,不经过任何服务器,真正做到了“说了什么只有自己知道”,私密性极佳。 使用体验方面,软件将极简操作贯彻到底。解压后双击 大飞哥语音输入,右下角托盘出现蓝色话筒图标即代表准备就绪。用户只需将光标点进目标输入框,按住右 Alt说话,说完松开,文字便会自动写入。主界面提供了设置快捷键与开机自动启动选项,方便用户按习惯自定义组合键;关闭主窗口并不会退出程序,而是缩至托盘持续待命,彻底退出只需右键托盘或点击主界面按钮,逻辑清晰,零门槛上手。

大飞哥软件自习室
评论
收藏 已收藏
分享
返回顶部