OO第二单元总结
作者:互联网
OO第二单元总结
目录总览
- 三次作业任务依次为单电梯、多电梯、换乘多电梯。
- 均使用了双层
生产者-消费者
模型。 - 后两次作业由于分配请求需要知晓电梯状态,引入观察者模式向分配器更新状态。
- (其中第一次作业由于为单电梯,省去了请求分发正确电梯线程)。
- 其余的两次作业均为4个线程,后文作具体解释。
第一次作业
本次任务为模拟单部电梯
类图
时序图
思路分析
第一层生产者-消费者
-
MyInput
为生产者,实时向托盘Scheduler
读入请求。 -
Scheduler
为托盘,采用单例模式,保存线程安全队列<Person>
与ArrivePattern
。 -
Updater
为消费者,为电梯子类,实时从托盘读出请求,待电梯在合适时间取用。
第二层生产者-消费者
Scheduler
为生产者,实时向Updater
输入请求。Updater
为托盘,保存请求数组。Elevator
为消费者,在到达每层时从托盘中取走该层出发的请求。
调度策略
- 在SSTF的基础上增加:当电梯内有人时,目标楼层为最近上楼或最近出楼层的。
同步块的设置与加锁
- 对两层
生产者-消费者
模型托盘(分别为Scheduler
和Updater
)的读写均需要同步处理。 - 特别的,
Elevator
对Updater
进行组合(读/写)操作时需要额外加锁(sychronized)。
复杂度分析
- 从图中可以看出,
Elevator
类中的move
方法复杂度较高,主要是因为其承担了结束线程的if条件判断较为复杂,导致方法复杂度的升高,现在看来可以将该判断单独抽象为一个方法降低复杂度。
Bug分析
本次作业强测和互测均未出现bug。
第二次作业
本次任务为模拟多部电梯
类图
时序图
思路分析
第一层生产者-消费者
-
MyInput
为生产者,实时向托盘Scheduler
读入请求。 -
Scheduler
为托盘,采用单例模式,保存线程安全队列<Person>
与ArrivePattern
。 -
Dispatcher
为消费者,也采用单例模式,实时从托盘中取出请求。
第二层生产者-消费者
Dispatcher
为生产者,按分配规则实时向各个电梯的Updater
输入不同请求。Updater
为托盘,保存请求数组。Elevator
为消费者,在到达每层时从托盘中取走该层出发的请求。
调度策略
- 单电梯策略不变。
- 调度器为请求指定电梯时,选择目前离出发点最近的电梯(这点导致了强测性能点的RTLE)
观察者模式
Elevator
在到达每层时向Dispatcher
发布自己的状态变化。
同步块的设置与加锁
- 对两层
生产者-消费者
模型托盘(分别为Scheduler
和Updater
)的读写均需要同步处理。 - 观察者模式中对
status
的读写需要同步处理。 Elevator
对Updater
进行组合(读/写)操作时需要额外加锁(sychronized)。- 观察者模式中
Dispatcher
对电梯状态进行组合(读/写)操作时需要额外加锁(sychronized)。 - 涉及新增电梯操作,对电梯数组
Elevators
的读写也需要同步处理。
复杂度分析
- 从图中可以看出,与第一次作业基本相同,
Elevator
类中的move
方法复杂度较高,主要是因为其承担了结束线程的if条件判断较为复杂,导致方法复杂度的升高,现在看来可以将该判断单独抽象为一个方法降低复杂度。
Bug分析
本次作业强测出现一处bug,有两个点未通过(唯二的性能点)。
-
原因也很简单,特殊情况性能太差,出现请求扎堆。
-
具体来说是我在分配请求给每个电梯时未考虑电梯已承担请求的数量,对单个请求只用是否独立最优来分配电梯,这导致了若同时到达大量类似请求(出发、到达楼层很近),会全部被分给同一电梯(请求扎堆),导致超时。
-
修复策略为将已承担请求作为分配标准之一:在多于某个数量后优先考虑其他电梯。
第三次作业
本次任务为模拟多部可换乘电梯
类图
时序图
思路分析
第一层生产者-消费者
-
MyInput
为生产者,实时向托盘Scheduler
读入请求。 -
Elevator
也为生产者,将换成乘客后重新放入Scheduler
托盘。 -
Scheduler
为托盘,采用单例模式,保存线程安全队列<Person>
与ArrivePattern
。 -
Dispatcher
为消费者,也采用单例模式,实时从托盘中取出请求。
第二层生产者-消费者
- 与上次作业相同
调度策略
- 单电梯策略不变。
- 调度器策略沿用上次bug修复时的策略。
- 换乘策略,设置3、9、15三个换乘点,手动打表设置所有情况的换乘路径(设置思路为在保证可以运送的基础上优先级C>B>A)(这20*20/2的大量数据也给我带来了一个抄写bug)。
观察者模式
- 与上次相同,
Elevator
在到达每层时向Dispatcher
发布自己的状态变化。
同步块的设置与加锁
- 虽然加入换乘策略,但不需要对
Person
进行多线程读写,因此与上次加锁策略相同。
复杂度分析
- 从图中可以看出,本次作业新增的复杂度过高的方法为
Dispatcher.getRandomId()
,该方法其实是上次bug修复时引入的考虑电梯已承担请求来分配请求导致的高复杂度,当时处于时间问题没有抽象为单独的方法,现在看来,可以以此降低此处圈复杂度。
Bug分析
本次作业强测出现一处bug,有两个点未通过,错误原因为数据抄写错误。
-
我是打表填写换乘策略,从Excel誊写到Idea时(FROM-9-TO-16)出现手误。
-
很巧错误地写成了在9楼换乘(整体路径为9-9-16,此时第一段由于每层我采取先下后上会被关在电梯里)
-
覆盖测试不够严密,没有使各个楼层情况完全独立,于是该错误情况被其他数据干扰携带顺利完成。
-
因此这个bug可以说是出在了我的覆盖性正确测试中。
关于测试
- 黑盒测试(自测、互测基本相同)
- 主要为功能正确性测试
- 随机生成输入很难覆盖到线程bug
- 自己构造,主要为密集输入(攻击线程安全
- 白盒测试(自测时关注的检查点为互测主要攻击点)
- 方法抽象,降低复杂度(很麻烦但确实很有必要)
- 每个
boolean
取值的情况都要覆盖到 - 关注死锁,静态检查上锁情况
- 尽量去锁化,能放锁的地方放,用流程保证正确性
可扩展性
这单元作业具有良好的可扩展性
- 后两次作业从类图来看架构完全相同
- 单电梯运行策略沿用三次作业
- 指令分配策略在第二次作业引入并沿用后两次作业
- 换乘与结束程序均不改变任何架构设计,采用从自行构造特殊输入请求(如Id=-1/-2),换乘时修改出发到达楼层,这体现了良好的扩展性。
心得体会
- 本单元第一次编写测评机,显著提高了测评效率,但仍有亟待完善之处。
- 多线程的bug具有随机性,需要采取更全面以及多次的测试策略。
- “大道至简”——最后一次作业发现不针对不同到达模式优化反而或许可以获得更高的性能分。
- 设计模式的学习与应用将极大地提高架构的安全性与可扩展性。
标签:OO,总结,请求,生产者,复杂度,作业,托盘,电梯,单元 来源: https://www.cnblogs.com/LNTisNotaTree/p/14710961.html