BUAA OO 第二单元作业总结
BUAA OO 第二单元作业总结
一、题目简介
此单元作业为模拟多线程实时电梯系统,第五次作业为模拟各楼座的纵向电梯,第六次作业模拟纵向和横向电梯,第七次作业增加乘客的换乘请求
二、架构模式
(一)第五次作业
各个类如下所示:
|- MainClass:主类
|- Controller:电梯管理器,用于将请求分配到各座的调度器
|- InputHandler:输入线程
|- Dispatcher:调度器
|- Elevator:电梯线程
|- Output:线程安全的输出类
UML类图如下:
线程间协作关系如下:
InputHandler
负责读入外部请求,将请求传入Controller
并将请求分配给各个楼座的Dispatcher
,最后由Elevator
线程从Dispatcher
中读取当前座的外部请求并开始运行、接人等操作。
调度器 Dispatcher
是线程间的共享对象,运用简单的生产者-消费者模型解决了这次作业。
(二)第六次作业
各个类如下所示:
|- MainClass:主类
|- Scheduler:总管理器
|- Controller:各座或各楼层电梯管理器
|- InputHandler:输入线程
|- Dispatcher:调度器
|- Factory:电梯工厂类
|- Elevator:电梯线程
|- VerticalElevator:纵向电梯类
|- HorizontalElevator:横向电梯类
|- Output:线程安全的输出类
UML类图如下:
线程间协作关系如下:
InputHandler
负责读入外部请求,将增加电梯的请求传入Scheduler
并将请求分配给对应楼座或楼层的Controller
,新建对应的Dispatcher
和Elevator
;将乘客请求传入Scheduler
并将请求分配给对应楼座或楼层的Controller
,各个Controller
根据顺序分配给对应的Dispatcher
,由Elevator
线程从Dispatcher
中读取当前座的外部请求并开始运行、接人等操作。
此次作业架构上和上次作业差异不大,只是多了一层管理器,在线程交互层面仍然是简单的生产者-消费者模型,在上次作业基础上迭代较为简单。
(三)第七次作业
各个类如下所示:
|- MainClass:主类
|- Scheduler:总管理器
|- Controller:各座或各楼层电梯管理器
|- InputHandler:输入线程
|- Dispatcher:调度器
|- Factory:电梯工厂类
|- Elevator:电梯线程
|- VerticalElevator:纵向电梯类
|- HorizontalElevator:横向电梯类
|- Person:可换乘的乘客请求
|- Output:线程安全的输出类
UML类图如下:
线程间协作关系如下:
此次作业相较上次作业增加换乘要求,增加了满足换乘要求的Person
类,将一个请求拆分成多个请求,同时使用流水线架构,在Elevator
中完成一部分,将未完成的请求通过Scheduler
再次分配到对应的Dispatcher
中,直到乘客最终到达目的地。
三、锁与同步块的设置
(一)第五次作业
此次作业中,我将调度器 Dispatcher
设为共享对象,在Dispatcher
类中的方法均带锁。
在电梯Elevator
类中,因为要对Dispatcher
中获取请求,就要对Dispatcher
进行修改,所以在Elevator
中只要对Dispatcher
有访问,不管是读还是写均对Dispatcher
设置了锁,相应的,在这些部分都设置了同步块。将同步块根据电梯的行为分成较小的块,防止电梯运行中长时间持有锁导致性能的下降。
在输出类Output
中,考虑到线程安全,对其中的输出方法进行上锁,避免不同线程同时使用输出导致一些不可描述的错误。
(二)第六次作业
此次作业架构与上次作业没有太大差异,对共享对象的访问均上锁。
(三)第七次作业
此次作业因为使用了流水线模式,电梯完成请求的一部分后要将未完成的请求重新通过管理器分配,所以在总管理器Scheduler
中对自己上了锁,避免新增指令和重新分配指令的冲突分配。
四、调度器设计
在三次作业中,调度器Dispatcher
其实只是一个存储当前楼座或楼层外部请求的管理器,调度算法均实现在电梯中,实现了look算法,在每一层判断是否有要出和要进的人,没有则按照外部请求中最先到达的,向其前进。调度器起到的只是一个生产者InputHandler
和消费者Elevator
之间的“货架”作用。
五、程序的bug
(一)第五次作业
第五次作业因为架构比较简单,实现起来没有什么问题,主要在不断完善算法
(二)第六次作业
第六次作业发现了横向电梯死循环的bug,在当前方向还有请求时,由于横向环形电梯的特殊性,会想当前方向不停运动,为了解决这个问题,在横向电梯类中设置了一个标记属性,当电梯空载运行时标记加一,到三时转换电梯方向。
(三)第七次作业
此次作业出现的bug为测试时的不仔细导致,在根据电梯id分配请求时将判断id的条件写反了,虽然不懂为什么这样还能通过一些测试点,只能说中测和强测的数据并不十分合理。
六、Hack策略
(一)第五次作业
此次作业较为简单,没有hack到。
(二)第六次作业
此次作业由于没有考虑到死循环的问题,将这个问题用来hack他人,能有一定效果。
(三)第七次作业
没有hack别人。
七、心得体会
-
共享对象的维护非常重要,对其的改写一定要加锁,同时要注意锁的顺序避免出现死锁
-
对对象的
wait
操作一定要注意唤醒 -
线程的结束条件要多注意,在第七次作业输入结束后但乘客还需换乘的情况下,不对线程做特殊处理可能会直接结束。
-
完备的课下测试很重要,在后两次作业中都是吃了没有进行完备测试的亏,尤其是第七次作业出现的非常明显的错误。