OO第二单元作业——魔鬼电梯
简介
本单元作业分为三次
第一次作业:第一次作业要实现单部简单电梯,停靠所有楼层,无载客容量,性能分考量电梯运行时间。
第二次作业: 第二次作业实现多部电梯,电梯数量由初始化设定,每部电梯都停靠所有楼层,有相同载客容量上限,性能分考量电梯运行时间。
第三次作业:第三次作业实现多部电梯,初始三部,可通过指令动态增加。共分为三类电梯,三类电梯停靠楼层、载客上限、运行时间(上下及开关门)均有所不同。性能分考量电梯运行时间与乘客等待时间。
设计策略
本单元三次作业均采用的是生产者-消费者模式,其中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第二单元作业——魔鬼电梯的更多相关文章
- oo第二单元——多线程魔鬼电梯
在初步认识了面向对象思想后,立刻进入了多线程的学习,本单元的难点主要是锁的理解,需要保证线程安全的同时防止死锁的发生,也要尽可能缩小锁的范围,提高性能.这一单元以电梯为载体,让我们从生活出发,从电梯运 ...
- 电梯也能无为而治——oo第二单元作业总结
oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...
- 【OO学习】OO第二单元作业总结
OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...
- OO第二单元——多线程(电梯)
OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...
- oo第二单元作业总结
oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...
- OO第二单元作业总结【自我反思与审视】
第二单元作业的完成史,就是一部心酸的血泪史…… 多线程的出现为我(们)打开一片广阔的天地,我也在这方天地摸爬滚打,不断成长!如果说第一单元之前还对Java语法有所了解的话,那么这单元学习多线程则完全是 ...
- 电梯模拟系统——BUAA OO第二单元作业总结
需求分析 官方需求 本次作业需要模拟一个多线程实时多电梯系统,从标准输入中输入请求信息,程序进行接收和处理,模拟电梯运行,将必要的运行信息通过输出接口进行输出. 本次作业电梯系统具有的功能为:上下行, ...
- 你电梯没了—OO第二单元作业思考
写在前面 这三次电梯调度作业,主要是学习多线程并行操作,对于各个线程的时间轴的把握,互相的配合与影响,通过使用锁来解决访问冲突等方面. 个人在学会Thread相关操作之外,写出来一些奇怪结构的诡异操作 ...
- 2019年北航OO第二单元(多线程电梯任务)总结
一.三次作业总结 1. 说在前面 对于这次的这三次电梯作业,我采用了和几乎所有人都不同的架构:将每个人当作一个线程.这样做有一定的好处:它使得整个问题的建模更加自然,并且在后期人员调度变得复杂时,可以 ...
随机推荐
- Flutter NotificationListener 监听列表的滚动
import 'package:flutter/material.dart'; import 'package:flutter_imagenetwork/flutter_imagenetwork.da ...
- VAST二月上线交易所,打通NGK各大币种之间通道!
1月20日,管理着超过8.7万亿美元资产的全球最大资产管理公司贝莱德似乎已批准其旗下两个相关基金--贝莱德全球分配基金公司和贝莱德基金投资比特币期货.提交给美国证券交易委员会的招股说明书文件显示,贝莱 ...
- 星盟全球投资副总裁DENIEL SOIBIM:如何激发创造力
丹尼尔·索比姆毕业于加州理工大学,2005年通过创建投资俱乐部对潜力公司进行天使投资,获得了美国Blue Run高层的重视,任营收专家评估师,为Blue Run项目提案做风险评估,09年与泰勒·亚当斯 ...
- SharedPreferences 数据传输中遇到的一些问题总结
原构想:MainActivity 设置两个按钮,btn1--跳转Main2Activity通过复选框组选择并提交,btn2--跳转Main3Activity通过RecycleView显示选择结果. 主 ...
- Spark + GraphX + Pregel
Spark+GraphX图 Q:什么是图?图的应用场景 A:图是由顶点集合(vertex)及顶点间的关系集合(边edge)组成的一种网状数据结构,表示为二元组:Gragh=(V,E),V\E分别是顶点 ...
- 微信小程序(七)-项目实例(原生框架 MINA转云开发)==02-云开发-配置
云开发:1.就是用云函数的型式来使用云存储和云数据库完成各种操作! 2.只关注调什么函数,完成什么功能即可,无需关心HTTP请求哪一套! 3.此模式不代表没有服务器,只是部署在云环境中 ...
- 微信小程序:页面生命周期
小程序生命周期分为应用生命周期和页面生命周期 1.Onload:页面加载时触发,一般在onLoad中发送异步请求来初始化页面数据. 2.onShow:页面显示时触发 3.onReady:页面初次渲染完 ...
- 微信支付/支付宝支付/银联支付,对比加总结(Java服务端)
今天来讲讲支付. 工作到现在,接入过好几个项目的支付,其中涉及到了微信支付.支付宝支付.银联支付. 三种支付的对接感受其实整体上大同小异.都遵循同一个流程: 1).商户APP向商户服务器请求生成订单 ...
- Spring Boot集成Springfox Swagger3和简单应用
摘要:Springfox Swagger可以动态生成 API 接口供前后端进行交互和在线调试接口,Spring Boot 框架是目前非常流行的微服务框架,所以,在Spring Boot 项目中集成Sp ...
- Pyqt5——带图标的表格(Model/View)
需求:表格中第一列内容为学生学号,为了突出学号的表示,在第一列的学号旁增加学号图标. 实现:(1)使用Qt的model-view模式生成表格视图. (2)重写代理(QAbstractItemDeleg ...