简介

本单元作业分为三次

第一次作业:第一次作业要实现单部简单电梯,停靠所有楼层,无载客容量,性能分考量电梯运行时间。

第二次作业: 第二次作业实现多部电梯,电梯数量由初始化设定,每部电梯都停靠所有楼层,有相同载客容量上限,性能分考量电梯运行时间。

第三次作业:第三次作业实现多部电梯,初始三部,可通过指令动态增加。共分为三类电梯,三类电梯停靠楼层、载客上限、运行时间(上下及开关门)均有所不同。性能分考量电梯运行时间与乘客等待时间。

设计策略

本单元三次作业均采用的是生产者-消费者模式,其中ElevReader类是生产者,Elevator是消费者,Scheduler是托盘(当然,是个会调度的智能托盘)。在这个模型下,ElevReader和各个Elevator对于Scheduler的访问是互斥的,Scheduler要负责通过一定的算法完成调度并且与ElevReader和Scheduler实时交互。在Elevator中循环的流程是:等待请求->判断是否要开门->进出人(可省)->判断下一步要运行的方向->运行一层,其中等待请求、判断是否开门、判断下一步运行的方向都需要与Scheduler进行交互。

第一次作业

实现方式

利用LOOK算法,电梯上下摆渡来接送乘客,每当所在的楼层有需要进电梯的乘客时便开门让该乘客进来,所在楼层有需要出去的乘客便开门让该乘客出去,电梯改变运行方向的条件是电梯运行方向上已经没有任何的请求(不需要上人或者下人)。

类图

 

代码规模

复杂度分析

分析:Scheduler知晓所有的请求,并且管理着请求队列(类似生产者消费者里的托盘),所有与请求队列有关的操作均在Scheduler里执行,因而不符合高内聚低耦合原则。

第二次作业

实现方式

第二次作业在第一次作业的基础上进行迭代,仍然采用的是LOOK算法。本来试图尝试为每一个请求去分配一个电梯的,但是由于对多个电梯同时运行时的唤醒机制掌握不熟、当电梯满员时的已经分配的任务是否需要返回到总请求队列等细节没有思考清楚,因此即使按照这个思路写了一版代码但最后并没有开始Debug,也没有采用这种方式,而是沿用第一次作业的代码写了一个多台电梯无脑抢指令、谁先到位则谁先接送人的智障LOOK调度。

类图

代码规模

复杂度分析

分析:为了线程安全,每个电梯并没有自己的队列,就连在电梯里的人也没有由电梯来管理,这就导致电梯需要频繁的和调度器进行交互,而调度器是无脑synchronized的,这就导致运行效率低下。同时,由于电梯里几乎所有操作都要经过调度器,于是耦合度较高的Scheduler成功把Elevator也给拉下了水。总而言之,这样的设计在总体的执行顺序上是近乎单线程的顺序执行(因为一次只能有一个电梯访问Scheduler),同时耦合度较高,但是优点在于Scheduler对外的接口明确,修改调度算法只需要在Scheduler内部进行修改即可。

第三次作业

实现方式

这次终于用上了本来打算在第二次电梯就使用的任务分配队列,具体的实现是这样的:每个电梯维护一个response队列(里面的所有人一定在同一层)和一个inElev队列,每当电梯到达一层时便更新一次response队列,判断电梯里的人和response队列里的人要去哪一层以及Requests队列里有没有更近可以去接的人,若有则更新response队列;然后取最近的目的地并往那个方向走一层。这种调度方法比起上一次的优点在于:1.避免了电梯一窝蜂去抢一条指令,但最终只会有一个电梯抢到,这种做法在荷载大的时候效率比较低。2.可以确保A电梯在忙完了15~20楼的所有任务后才下-3~1楼,如果采用LOOK算法的话必定会出现A电梯来回奔波的惨象,导致有些测试点性能分低。

关于换乘,我采用的是静态换乘(类似打表的方式),换乘表格如下:

类图

代码规模

复杂度分析

分析:本次设计在电梯类中维护了inElev队列和response队列,调度器只维护总队列和负责分配,这样子提高了运行效率,使得并发执行的程度更高,但是由于每个楼层更新的算法复杂度较高不可避免导致数据飘红,但是总体设计上比上次有较大改善。

UML协作图

Bug分析

自己的BUG:三次作业在中测、强测中均没有发现Bug,但是第三次作业由于我误以为新增电梯的名称只能是X1 X2 X3中的一个导致在互测中被人疯狂HACK这个问题。

但是我于第一次作业Hack他人1次,第三次作业Hack他人2次。发现他人Bug的主要途径有三类:

1.在自己做作业的时候曾经犯下的错误、自查到Bug的测试数据记录下来用于Hack他人。

2.利用数据生成器生成一些暴力数据,对于功能进行全面检测,尝试找到他人程序中一些功能性漏洞。

3.自行编写特殊数据(例如多个相同起止楼层的人测超载,最后一条指令是加入电梯的指令等等)。

扩展性分析

1.SRP(Single Responsibility Principle)——单一职责原则

​在前两次作业中,由于电梯没有自己所维护的队列,所以把所有队列都放在Scheduler,导致Scheduler比较复杂,但是第三次作业中Scheduler只负责分配任务和其他线程安全的控制,维护队列、上下楼层均交给电梯来完成,ElevReader类只负责获取请求,这样的做法职责分明,对外接口清晰,符合单一职责原则。

2.OCP(Open Close Principle)——开闭原则

​从第一次作业到第二次作业只添加了部分内容,第二次作业到第三次作业由于调度算法不同以及队列位置不同修改了一些接口,但这都是主动的代码架构调整而非写不下去时被迫的重构,是可以接受的。对于第三次作业的代码,如果新增需求,比如要关闭电梯的话只需要在调度器中退出条件多加一条即可,再比如如果要按照乘客总重量来限制载重量只需要修改调度器中的分配策略即可,所以在OCP方面我的设计是比较好的。

3.LSP(Liskov Substitution Principle)——里氏替换原则

​本次作业没有采用继承的方式,无需进行LSP分析。

4.ISP( Interface Segregation Principle)——接口隔离原则

​本次作业没有采用接口的方式,但是所有的类中的public方法均提供相对独立的操作,符合LSP原则。

5.DIP(Dependency Inversion Principle)——依赖倒置原则

​Scheduler类中有调度,Elevator类负责执行,而由于信息的传递是单方向的(由Scheduler类发出指令指挥Elevator)所以对于电梯参数的修改不会影响到Scheduler,符合DIP原则。

心得体会

通过这一单元的学习,我有了很大的收获。在三次作业中不断尝试新的调度策略,最终在最后一次作业中取得了接近满分的性能分。此外,我还学会了自行编写对拍器。对拍器的数据生成器主要负责生产对应的数据,可供选择的生成数据由边界数据、多换乘数据、大荷载量数据、密集数据、全覆盖数据、纯随机数据,对拍器还有一个OnTimeInput的java文件负责定时向程序中投放输入数据,还有一个checker的jar包负责检查运行结果是否正确。经过一系列覆盖测试我确保了我在线程安全上以及换乘上没有遗漏,做完这些以后,强测便顺理成章的全部通过。关于线程安全的问题,由于我在设计的时候第一轮设计大框架,第二轮补充具体细节,因此在第二轮设计的时候我便已经把线程安全以及唤醒机制纳入到了考虑范围,针对唤醒以及各个线程之间的联系做了细致而全面的规划,从设计上杜绝了Bug的产生,这样的设计方法使我几乎没有出过线程安全的Bug(经历了强测、互测以及我自己评测机多个晚上跑都没有出Bug因此可以认为没有Bug)。综上,总体而言对于这一单元的作业我总体是比较满意的。

OO第二单元作业——魔鬼电梯的更多相关文章

  1. oo第二单元——多线程魔鬼电梯

    在初步认识了面向对象思想后,立刻进入了多线程的学习,本单元的难点主要是锁的理解,需要保证线程安全的同时防止死锁的发生,也要尽可能缩小锁的范围,提高性能.这一单元以电梯为载体,让我们从生活出发,从电梯运 ...

  2. 电梯也能无为而治——oo第二单元作业总结

    oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...

  3. 【OO学习】OO第二单元作业总结

    OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...

  4. OO第二单元——多线程(电梯)

    OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...

  5. oo第二单元作业总结

    oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...

  6. OO第二单元作业总结【自我反思与审视】

    第二单元作业的完成史,就是一部心酸的血泪史…… 多线程的出现为我(们)打开一片广阔的天地,我也在这方天地摸爬滚打,不断成长!如果说第一单元之前还对Java语法有所了解的话,那么这单元学习多线程则完全是 ...

  7. 电梯模拟系统——BUAA OO第二单元作业总结

    需求分析 官方需求 本次作业需要模拟一个多线程实时多电梯系统,从标准输入中输入请求信息,程序进行接收和处理,模拟电梯运行,将必要的运行信息通过输出接口进行输出. 本次作业电梯系统具有的功能为:上下行, ...

  8. 你电梯没了—OO第二单元作业思考

    写在前面 这三次电梯调度作业,主要是学习多线程并行操作,对于各个线程的时间轴的把握,互相的配合与影响,通过使用锁来解决访问冲突等方面. 个人在学会Thread相关操作之外,写出来一些奇怪结构的诡异操作 ...

  9. 2019年北航OO第二单元(多线程电梯任务)总结

    一.三次作业总结 1. 说在前面 对于这次的这三次电梯作业,我采用了和几乎所有人都不同的架构:将每个人当作一个线程.这样做有一定的好处:它使得整个问题的建模更加自然,并且在后期人员调度变得复杂时,可以 ...

随机推荐

  1. 翻译:《实用的Python编程》01_07_Functions

    目录 | 上一节 (1.6 文件) | 下一节 (2.0 处理数据) 1.7 函数 随着程序开始变大,我们会想要有条理地组织这些程序.本节简要介绍函数.库模块以及带有异常的错误处理. 自定义函数 对你 ...

  2. 开源OA办公平台功能介绍:应用市场-固定资产管理(一)功能设计

    概述 应用市场-固定资产管理,是用来维护管理企业固定资产的一个功能.其整个功能包括对固定资产的台账信息.领用.调拨.借用.维修.盘点.报废等一整个生命周期的动态管理过程.力求客户安装就可以使用. 本应 ...

  3. linux系统忘记root的登录密码

    参考链接:https://www.jb51.net/article/146541.htm  亲测有效 使用场景 linux管理员忘记root密码,需要进行找回操作. 注意事项:本文基于centos7环 ...

  4. ElasticSearch 集群安全

    公号:码农充电站pro 主页:https://codeshellme.github.io 在安装完 ES 后,ES 默认是没有任何安全防护的. ES 的安全管理主要包括以下内容: 身份认证:鉴定访问用 ...

  5. 02.从0实现一个JVM语言之词法分析器-Lexer-03月02日更新

    从0实现JVM语言之词法分析器-Lexer 本次有较大幅度更新, 老读者如果对前面的一些bug, 错误有疑问可以复盘或者留言. 源码github仓库, 如果这个系列文章对你有帮助, 希望获得你的一个s ...

  6. 【图像处理】使用OpenCV+Python进行图像处理入门教程(二)

    这篇随笔介绍使用OpenCV进行图像处理的第二章 图像的运算,让我们踏上继续回顾OpenCV进行图像处理的奇妙之旅,不断地总结.回顾,以新的视角快速融入计算机视觉的奥秘世界. 2  图像的运算 复杂的 ...

  7. 面试题-python 如何读取一个大于 10G 的txt文件?

    前言 用python 读取一个大于10G 的文件,自己电脑只有8G内存,一运行就报内存溢出:MemoryError python 如何用open函数读取大文件呢? 读取大文件 首先可以自己先制作一个大 ...

  8. ArrayList源码分析笔记

    ArrayList源码分析笔记 先贴出ArrayList一些属性 public class ArrayList<E> extends AbstractList<E> imple ...

  9. java关于字符串是否存

    1, if('true'.equalsIgnoreCase(response.result as String)); 2,   if (scvrsp.toLowerCase().contains(&q ...

  10. AtCoder Beginner Contest 192

    A Star #include <cstdio> using namespace std; int n; int main() { scanf("%d", &n ...