北航OO(2020)第二单元博客作业
第二单元第一次作业
多线程设计策略
第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程。输入线程获取请求并发送给调度器线程;调度器线程通过查询电梯线程的状态(等待、停靠以及移动),并综合已有的请求为电梯线程分配目标;电梯线程根据分配到的目标进行移动,并进行上下乘客操作。
为了这么做输入线程与电梯线程有一共享变量requestQueue用以保存请求,调度器线程与电梯线程有一共享变量taskQueue用来保存电梯的移动目标。这种设计的初衷是让不同线程尽量只干自己该干的事,即输入线程负责请求的输入,调度器线程负责请求的调度,电梯线程负责模拟电梯运动以及人员出入。这么做的好处一方面在于各个线程的职责都十分明确,另一方面在于调度器便于实现更加灵活的调度策略。但这么做的问题在于调度器访问得到的电梯线程状态与电梯线程实时状态并不是同步的,所以在调度器类以及电梯类中都需要加入较多语句来保证调度器能够及时地为电梯更新目标,以及电梯能够正确处理目标。同时另一个问题就是调度器主动分配目标的做法在多部电梯时需要考虑的情形较多,处理起来比较复杂。
度量分析
方法复杂度(其中较复杂的几个)
类复杂度
类图
协作图
本次作业的复杂度主要集中在AlsElevator类以及ElevatorController类中,前者主要与收集电梯运动状态,以及控制运动有关,后者主要与根据不同情况分配目标有关,算是在保证不同类有有不同分工的同时分散了复杂度。同时预留了一个Elevator接口,本来以未之后的作业会加入不同类型的电梯,所以预留接口一边迭代,但由于没有考虑清楚作业的扩展方向,所以最终都没有使用上这个接口。
BUG相关
本次作业在本地测试时一开始遇到的bug主要是线程的退出问题,原先的做法是电梯在无目标时,则睡眠以等待调控器分配目标,但最初由于处理不当导致电梯线程对最终的终止信号响应失败,最终通过增加额外判断,解决了这一问题。
在互测方面,还是以自动测试为主,由于多线程细节方面有很多线程安全的问题,所以自动测试的随机性能取得一定的效果。
心得体会
作为第一次多线程作业,这次作业的起点对我来说还是比较高的。且由于吃了设计缺陷的亏,使得自己在保证线程同步方面耗费了很多精力,也算是告诫自己要先考虑好设计在进行程序的实现吧。
第二单元第二次作业
多线程设计策略
由于这次作业需要完成多部电梯的设计,所以为了降低调度器与电梯之间的耦合度,讲任务分配方案改为电梯主动从调度器获取目标。每个电梯维护自身的目标队列,当目标队列为空则睡眠等待调度器唤醒。调度器负责在有新的请求到来时或者请求未被完全处理完但有电梯在睡眠时向电梯发送通知。当调度器唤醒了多部电梯时,电梯间竞争获取请求。
通过这种处理,较第一次作业来说极大地减少了调度器线程与电梯线程之间的耦合度,并因此大大降低了保证线程间同步的难度。
度量分析
方法复杂度
类复杂度
类图
协作图
从复杂度来看,这次作业相比与第一次作业很明显的就是复杂度集中到了Elevator类中,这是因为Elevator类既需要完成电梯运行的模拟还需要为处理如何获取请求以及请求应该如何安排的问题。从具体的方法复杂度也可以看出Elevator类中关于如何将得到的请求安排到目标队列的方法占据了复杂度的很大一部分。虽然改变了控制策略,但整个程序在大体上也延续了第一次作业中的结构。
BUG相关
在做第二次作业时本地遇到的bug是没有分电梯是否满人来进行请求的处理,出现的情形是电梯已经满人无法继续接受请求,但安排任务的函数没有考虑到这一点而不断为电梯安排这个无法处理的请求导致的。
在互测阶段由于对大多数人来说,这次作业相较于上次作业的只是简单的增加了几部电梯,并且在第一次作业中已经积累了一定的经验,所以这次的尽管也是采用自动测试的模式,但挂了相当长一段时间都没有成功找到他人的bug。
心得体会
虽然大部分人这次作业都延续了上一次作业的风格,但由于我这一次改变了整个控制的策略,就导致需要在Elevator类以及ElevatorController类中做较多变动。但也算是及时止损吧,如果按照上一次作业的思路,那这次作业调度器需要考虑问题复杂度对于我自己来说应该是非常大的。虽然最后程序的复杂度被集中到了Elevator类中,但整体实现起来的难度却相较于上一次作业有所下降。
第二单元第三次作业
多线程设计策略
本次作业对上一次策略进行了较大的延续,但也做了几处更新。第一个更新是对换乘的支持,我才用了一种比较妥协的换乘策略,即自己实现了一个请求类,并将所有请求都分为两步:第一步是能进入哪种电梯,第二步是能从哪种电梯中出来,然后根据进入电梯的情况,有这个自定义的请求类自己生成出电梯的楼层。这样做的话每个请求的运载就被分为两种情形:如果第一次进入的电梯可以直接送达就直接让这个电梯送达,如果不能则请求类的目的楼层为某一个中转楼层以便第二次运输就可以到达终点。第二个更新是电梯的运行方式,之前是以目标队列中下一个目标来进行移动,而这次更新为将目标队列进行升序排列,然后始终往队列两端中离当前楼层较近的一段移动,这种方法较前一种方法可以较少很多细节方面的实现,所以性能可能有所损失,但是起来简单很多。最后一个更新是调度器在给电梯通知的同时会多加一个可选的目的层以帮助电梯减少决策的复杂度。
度量分析
方法复杂度
类复杂度
类图
协作图
对于这次作业新增的换乘请求,我的想法是想模仿人自己处理换乘操作,所以新建了一个MyRequest类,用来处理与换乘相关的操作。由于新建的这个类分担了很多很多有关目标处理的操作,同时也对电梯运行的策略进行了简化,所以在这次作业中几个类相较于上一次作业复杂度都有了一定程度的降低,特别是Elevator类,这个类基本回归了第一次作业的角色,主要处理电梯移动的模拟。除了新增的MyRequest类以外,其他几个类之间的相互关系跟第二次作业相近。
BUG相关
本次作业在一开始在本地测试发现了这样一个bug:由于我电梯的设计是当不再有请求,同时请求队列为空时会开始向下传递终止信号,等电梯运送完这次任务就结束程序,但有时候电梯内部可能有需要换乘的人员,这就会导致人员未被送完程序就结束了。
另一个bug是在互测阶段产生的,最后发现是一个死锁的bug:电梯与调度器之间有一个互相唤醒的关系存在,当电梯状态更新,或是有新请求到来时,调度器会被唤醒;当调度器发现有未处理请求且有电梯在休眠时,调度器会唤醒电梯。但这样一个关系存在一个问题,就是当电梯正好要去睡眠前,调度器检查发现无可调度电梯于是进入睡眠,但下一刻电梯恰好都去睡眠了,于是就会出现互相等待对方的死锁局面,最终通过设置wait时间上限来向这一bug妥协。
心得体会以及总结
这次作业通过MyRequest类的增加,我认为在一定程度上平衡了不同类的复杂度,同时也较上一次作业对类之间的职责有了清晰一点的划分。但我这三次作业中都没考虑过的一个问题就是调度器是否需要单独设置成一个进程的问题,现在考虑来看由于调度器的操作场景完全是在外界状态有更新时(包括有新的请求、电梯状态更新等)才需要进行操作,所以完全可以不需要设置成一个进程,直接让输入进程与Elevator进程把调度器当作一个共享资源来操作即可,同时这样做还有一个好处就是可以不在考虑电梯线程与调度器线程之间的同步问题,也能在一定程度上使得实现更简洁也更安全。
对于程序的扩展性,考虑了临时删除电梯以及乘客中途更换目的的情况。临时删除电梯可以通过将电梯从电梯集中删去,并通过endSignal信号让电梯在运送玩这次任务后就结束来实现。对于乘客临时更换目的地,由于MyRequest类就是一个模仿乘客行为的类,所以通过更改MyRequest对象的一些属性就可以简单地实现乘客的临时换乘。
北航OO(2020)第二单元博客作业的更多相关文章
- OO第四单元博客作业
OO第四单元博客作业 BUAA_1706_HugeGun 目录 第四单元作业架构设计 四个单元架构设计及OO方法理解 四个单元测试理解与实践演进 课程收获 一点建议 第四单元作业架构设计 ### 第十 ...
- oo第三单元博客作业
JML语言理论基础 Java建模语言(Java Modeling Language,JML)是一种进行详细设计的符号语言,他鼓励你用一种全新的方式来看待Java的类和方法.JML是一种行为接口规格语言 ...
- OO第二次博客作业—17373247
OO第二次博客作业 零.写在前面 OO第二单元宣告结束,在这个单元里自己算是真正对面向对象编程产生了比较深刻的理解,也认识到了一个合理的架构为编程带来的极大的便利. (挂三次评测分数 看出得分接近等差 ...
- OO第二次博客作业——电梯调度
OO第二次博客作业——电梯调度 前言 最近三周,OO课程进入多线程学习阶段,主要通过三次电梯调度作业来学习.从单部电梯的傻瓜式调度到有性能要求的调度到多部电梯的调度,难度逐渐提升,对同学们的要求逐渐变 ...
- OO第4次博客作业
OO第4次博客作业 一.第4单元设计 第四单元主要围绕UML图的结构进行JAVA代码编写,对JAVA的层次结构进行更多的认识.个人认为编程操作在实质上与上一章的PathContainer有许多的相同之 ...
- BUAA 2020 软件工程 个人博客作业
BUAA 2020 软件工程 个人博客作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 ...
- [BUAA OO]第三次博客作业
OO第三次博客作业 1. 规格化设计的发展 我认为,规格化设计主要源自于软件设计的两次危机.第一次是由于大量存在的goto语句,让当时被广泛应用的面向过程式的编程语言臃肿不堪,在逻辑性上与工程规模上鱼 ...
- OO第三次博客作业——规格
OO第三次博客作业——规格 一.调研结果: 规格的历史: 引自博文链接:http://blog.sina.com.cn/s/blog_473d5bba010001x9.html 传统科学的特点是发现世 ...
- OO第四次博客作业!
oo第四次博客作业 一.测试与正确性论证比较 测试只是单方面片面的证明对于当前的输入程序是正确的,测试只能证明程序有错误,不能说明程序是对的. 正确性论证是程序达到预期目的的一般性陈述,是通过规范化的 ...
随机推荐
- ECMAScript 2018(ES9)新特性简介
目录 简介 异步遍历 Rest/Spread操作符和对象构建 Rest Spread 创建和拷贝对象 Spread和bject.assign() 的区别 正则表达式 promise.finally 模 ...
- Android学习之在Adapter中调用Fragment
•前言 在学习<第一行代码>,4.5 小节--一个简易版的新闻应用的时候: 在为 RecyclerView 创建适配器的时候: 作者直接在 NewsTitleFragment.java 中 ...
- [树状数组]数星星 Stars
数 星 星 S t a r s 数星星 Stars 数星星Stars 题目描述 天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标.如果一个星星的左下方(包含正左和正下)有 k k k 颗星星 ...
- Redis主从&哨兵集群搭建
主从集群 在搭建主从集群前,我们先把Redis安装起来: #解压Redis压缩包 [root@master lf]# tar -zxvf redis-6.2.1.tar.gz -- #安装gcc [r ...
- [.net] 关于Exception的几点思考和在项目中的使用(一)
本文链接 https://www.cnblogs.com/hubaijia/p/about-exceptions-1.html 关于exception的基本语法和作用,这里不再赘述,下面记录一下我在项 ...
- 一文彻底掌握Apache Hudi的主键和分区配置
1. 介绍 Hudi中的每个记录都由HoodieKey唯一标识,HoodieKey由记录键和记录所属的分区路径组成.基于此设计Hudi可以将更新和删除快速应用于指定记录.Hudi使用分区路径字段对数据 ...
- 【Azure 微服务】Service Fabric中微服务在升级时,遇见Warning - System.Collections.Generic.KeyNotFoundException 服务无法正常运行
问题描述 使用.Net Framework 4.5.2为架构的Service Fabric微服务应用,在升级后发布到Azure Fabric中,服务无法运行.通过Service Fabric Expl ...
- Dynamics CRM安装教程七:Claims-based认证-内部访问配置
DFS安装配置好后就要开始配置CRM基于内部认证访问的配置,即使用HTTPS在CRM服务器进行访问的设置.在CRM服务器中找到Dynamic CRM部署管理器,开始菜单选择Dynamic CRM部署管 ...
- 『Spring Boot 2.4新特性』减少95%内存占用
节省 95%的内存占用,减少 80%的启动耗时. GraalVM 是一种高性能的虚拟机,它可以显著的提高程序的性能和运行效率,非常适合微服务.最近比较火的 Java 框架 Quarkus 默认支持 G ...
- 「性能提升」扩展 Spring Cache 支持多级缓存
为什么多级缓存 缓存的引入是现在大部分系统所必须考虑的 redis 作为常用中间件,虽然我们一般业务系统(毕竟业务量有限)不会遇到如下图 在随着 data-size 的增大和数据结构的复杂的造成性能下 ...