BUAA_OO_2020_Uint2_Summary
- BUAA_OO_2020_Uint2_Summary_Dir
- 设计策略(线程分工与调度策略)
- 第一次作业
- 第二次作业
- 第三次作业
- 第三次作业架构设计的可扩展性分析
- 程序结构度量分析
- Bug分析
- 心得体会
- 设计策略(线程分工与调度策略)
一、设计策略
第一次作业
线程分工:
Myinput,Elevator,Dispatcher三个线程。
Myinput与Dispatcher间共享乘客请求类Requests,Dispatcher与Elevator间共享电梯内乘客类peopleinside。
Myinput线程:解析乘客请求放入Requests并呼叫Dispatcher线程。
Elevator线程:维护电梯运行状态,完成上下楼层功能,每到一层楼呼叫Dispathcer线程等待运行状态更新及乘客上下电梯。
Dispatcher线程:根据电梯运行状态及Request执行调度算法,更新电梯的运行状态并完成上下乘客功能。根据Myinput线程及电梯运行状态结束Elevator线程,之后结束运行以实现退出。
调度策略:
在单部、可稍带、不限容量条件下,最终所有的乘客请求都需该部电梯自己响应,我将电梯的运行策略分为两种:寻找上(下)行乘客中楼层最低(高)者,以其楼层作为起点运行至上(下)行乘客中最高(低)楼层。选择两种策略中运行总距离较短的一个执行,在前往起点的过程中不进行捎带,在由起点到终点的过程中只有遇到有顺风乘客或有人要下车时开门,而一旦开门便接受该层所有乘客,根据同向乘客目的地更新最远目的地。最终性能表现较优异。
第二次作业
线程分工:
Myinput,Elevator,Dispatcher三个线程。
Myinput与Dispatcher间共享乘客总请求类MainRequests,Dispatcher与Elevator间共享电梯分请求类Requests。
Myinput线程:解析乘客请求放入总请求类MainRequests并呼叫Dispatcher线程。
Elevator线程:维护电梯运行状态,基于内部请求类Requests完成电梯所有功能,并每到一层楼呼叫Dispathcer线程等待请求分配。
Dispatcher线程:根据电梯运行方向、所处楼层、负载情况及Request执行调度算法,为不同电梯分配请求。根据Myinput线程及电梯运行状态结束Elevator线程,之后结束运行以实现退出。
调度策略:
对每一乘客请求计算不同电梯响应距离:若乘客与电梯所行方向相同且在电梯与目的地之间则取其据电梯目前楼层间距离,否则取其与电梯目的地之间距离+电梯目前楼层据电梯目的地之间距离。根据响应距离择近分配,电梯空闲下来时取最远请求执行。
该调度策略综合考虑了乘客等待时间及总运行时间,最终性能表现优异。
第三次作业
线程分工:
Myinput,Elevator,Dispatcher三个线程。
Myinput与Dispatcher间共享乘客总请求类MainRequests,Dispatcher与Elevator间共享电梯分请求类Requests及总请求类MainRequests。
Myinput线程:解析乘客请求放入总请求类MainRequests并呼叫Dispatcher线程。
Elevator线程:维护电梯运行状态,基于内部请求类Requests完成电梯所有功能,并每到一层楼呼叫Dispathcer线程等待请求分配。
Dispatcher线程:根据电梯运行方向、所处楼层、负载情况及Request执行调度算法,为不同电梯分配请求。根据Myinput线程及电梯运行状态结束Elevator线程,之后结束运行以实现退出。
调度策略:
扩展了乘客Person类功能,设置目的楼层cache及换乘标记实现乘客从分请求队列回归总请求队列的功能,其余调度策略同第二次作业。由于第二次作业调度策略的综合考虑,本次最终性能表现依然优异。
二、第三次作业架构设计的可扩展性分析(SOLID原则)
SRP——单一职责原则
由于第一次作业中只有一部电梯,分派线程并没有存在的必要,于是我的Dispatcher类执行的其实是电梯内部的调度任务,完全可以和Elevator正好到一起。我在第二次作业中发现了这一问题,并做出了修改,并最终沿用到第三次作业当中。
其中Elevator类只负责根据内部请求进行内部调度及电梯运行,Dispatcher类只负责根据主请求队列及电梯状态进行请求分派,Myinput类只负责解析输入放入主请求队列。三种类各司其职没有职责重合,符合SRP原则。
OCP——开放封闭原则
从第二次作业到第三次作业的扩展来看,除去增添Elevator线程向总请求类放回换乘乘客功能时对下车函数增加了五行以内的代码之外,已有实现没有做出其他更改,其他修改也仅仅是对Person类的小扩展,这也使我第三次作业仅仅耗时不到一小时就以优异的性能表现完成。
而基于SRP原则的考虑设计,我的代码在不需修改调度策略的前提下可以很好的符合OCP原则。
LSP——替换原则
在第三次作业当中三类电梯我均通过继承Elevator类实现,而覆写的方法也只有初始化时对负载、上下楼层时间的赋值,以及可到达楼层的判断不同,结合工厂模式创造电梯对象,可以很好的满足LSP原则。
ISP——接口隔离原则
本单元作业中我并未使用除RUNNABLE接口以外的其他接口,但各个类无覆盖功能,所有public方法的操作均相对独立,符合ISP原则。
DIP——依赖倒置原则
除却对于共享资源MainRequests类、Requests类的访问及对于电梯类的状态检验外,各个线程均不会调用其他线程类的方法,没有循环依赖,符合DIP原则。
三、程序结构度量分析
第一次作业
uml类图:
复杂度分析:
第二次作业
uml类图:
复杂度分析:
第三次作业
uml类图:
复杂度分析:
分析:
第一次作业中Dispatcher类实际为电梯内部调度器,设计定位不清导致冗余。第三次作业基本延续了第二次作业的架构,可扩展性及功能划分都令我比较满意,但为了实现自己设计的优化策略引入了很多高复杂度算法,导致总体代码复杂度很高,还有很大的改进需求以及重构的必要。
时序图:
四、BUG分析
我自己的BUG:
本单元作业仅在第二次作业的强测部分出现一个BUG,但是错误很难复现,加入调试方法后似乎对线程运行产生了影响导致错误更难复现了,我裂开。
而在我锲而不舍地思索了一个星期以后发现输入线程在没有被锁住的情况下谜之停顿很长时间不运转,求助了很多有相同问题的同学但目前仍未有人解决,我绝望。
本单元自始至终我都采用搭建自动评测机的方法来进行debug与自我测试,而自动评测机的价值也在本单元崭露出来,成为了我debug的一大利器。另外也从其他同学身上学习到了使用
Jprofiler、Jconsole、Jstack监控线程状态有效调试技巧。
而这次bug难以复现的经历也为我搭建评测机带来了新的警示,要尽可能模拟课上强测评测环境来搭建评测机!
HACK策略:
我的自动评测机在本单元的互测阶段大展神威,帮助我找到了很多bug,这也显示出在自我测试阶段经历自动评测机洗礼的重要性!
五、心得体会
从单线程到多线程,初入电梯单元所受到的冲击的确无愧于魔鬼二字,但是在第二次到第三次作业中仅仅使用40分钟左右便完成扩展的痛快体验亦印证着多线程学习给我带来的慢慢收获,这一路上我细扣过代码中每一步锁的传递、苦思冥想过优化算法中若干个状态间的起承转合,也遇见过很多奇奇怪怪的bug、初次在计算机领域领略了测不准原理这样的玄学问题,满心疲惫但是分外值得!