BUAA_OO_2022第二单元总结
第五次作业
A-E座每层单部电梯,电梯可以在1-10层运行。
1.总体架构设计
本次作业设置了总共4个线程类,分别为主线程,输入线程,调度器线程和电梯线程。由于每座单部电梯,调度器的设计较为简单,无特殊调度策略。电梯调度使用LOOK策略,策略由ElevController
中方法生成,具体策略如下:
-
每当电梯到达一层后,获取下一方向。
-
当前方向上若有未完成的请求,则不改变方向,否则,改变方向。
-
运行过程中尽可能捎带。
I. UML类图
II.UML协作图
2.线程同步处理
主要的共享对象是总调度队列、电梯调度队列和电梯乘客队列。以下是同步处理:
-
使用继承自
LinkedBlockingQueue
的RequestQueue
类实现总调度队列和电梯调度队列,由于其自带的同步访问特性,省略了在put
和take
操作时同步锁的添加。 -
电梯人员出入的检测和电梯下一方向的选择操作时,对电梯调度队列和乘客队列加上
synchronized
同步锁。 -
输入线程结束时,向主队列添加一特殊请求
new PersonRequest(0, 0, '0', '0', 1)
,调度器线程调度完其他请求并获取到该请求后,退出循环并关闭所有电梯的等待队列,电梯线程在等待队列空、乘客空且队列关闭后结束,程序结束。 -
创建输出类,将输出套一层同步锁确保输出时间戳同步。
第六次作业
增加横向电梯,支持电梯的增添,请求限制在同座或同层。
1.总体架构设计
本次作业中,为简化线程个数和复杂度,将调度器线程与输入线程合并,调度器类Dispatcher
作为普通类,提供一些调度相关的静态方法。在输入线程调用这些方法用于调度请求。另外,增加ElevDispatcher
类,提供静态方法用于处理电梯的增添请求。
电梯策略方面,为增加性能并支持横向电梯的LOOK策略,将下一方向的生成操作从每次到达一层进行,改为每次开关门后进行。横向电梯LOOK策略与纵向基本相同,区别如下:
-
请求方向的判断:若请求位于当前位置顺时针移动1-2位,则判定请求位于顺时针方向,反之亦然。
调度器策略方面:初始考虑使用策略分配,将请求优先分配给距离最近的电梯,但由于实现时bug过多,在第七次作业中更改为随机分配。
I.UML类图
II.UML协作图
2.线程同步处理
大体的同步处理与第五次作业相同,为适应调度器线程的移除进行如下改动:
-
输入线程检测到
null
时,表示所有请求均已调度完毕,此时关闭所有等待队列。 -
电梯线程在等待队列空、乘客空且队列关闭后结束。
第七次作业
横向电梯增加可达楼座限制,电梯容量和速度可定制,请求可以是不同楼座和不同楼层。
1.总体架构设计
本次作业需要增添一个是否需要换乘的判断。为此,我额外建立了一个ComplexPersonRequest
类,继承PersonRequest
类,内设属性progress
和total
,其中前者表示当前进度,后者表示总步骤数量,total
最大为3。
在调度每一条请求时判断若需要换乘,则创建ComplexPersonRequest
对象,代替PersonRequest
对象进行调度和执行,每当执行完一个步骤后,调用Dispatcher.dispatch()
方法,继续寻找合适的电梯进行请求分配。后来反思发现,如果将所有请求均用新类的对象代替,相比我仅仅将需要换乘的请求创建ComplexPersonRequest
对象的方法,可以减少许多类型特判,增加鲁棒性,使代码更加清晰。
电梯的运行策略与前一次作业相同,调度横向电梯的选取时,判断若
((switchInfo >> (from - 'A')) & 1) + ((switchInfo >> (to - 'A')) & 1) == 2
则判定为可运送。
若存在多部可运送电梯,由于原先的策略分配设计有误,导致RTLE,故调度策略改为随机分配。
I.UML类图
II.UML协作图
2.线程同步处理
由于输入线程要反复调用Dispatcher.dispatch()
方法来调度不同阶段的请求,在输入结束时调度还未结束。所以在输入结束时输入线程增加了wait()
等待,直到电梯处理完最后一个请求后将其唤醒,然后输入线程再关闭等待队列,结束线程。
为实现上述过程,在ComplexPersonRequest
类中加入token
属性,表示正在处理中的请求数量。由于token
是静态属性,token
加减的时候需要把整个类加锁。
二、bug分析
1.个人bug
-
第五次作业强测顺利通过。
-
第六次作业中,由于缺乏对电梯满员的方向判断,导致部分强测点RTLE。
-
第七次作业中,由于没有考虑到同层请求也需要换乘,出现了严重bug,导致了强测大量点WA,同时互测也被hack了此处。
以上bug的修复都比较简单,加上对特定条件的判断即可,可见自己的思维严谨性还需加强。
2.发现他人bug的策略
-
使用随机生成的数据。
-
构造边界数据,如69.9s时输入,大量同层请求,大量同座请求,超载请求等。
三、心得体会
1.线程安全
本单元的设计过程中,我花了大量的时间在理解和调试线程安全相关的工作。从第五次作业理解掌握同步块的含义到本单元完成,线程安全都是极为重要的一点,如何协调各线程,是主要考虑的问题。相比之下,逻辑实现的难度相对低。在完成了本单元的三次作业后,我对多线程设计的理解变得更加清晰,提升明显。
但由于时间和水平所限,我没能尝试使用课上讲解的更加高性能的读写锁和一些其他设计模式,有点小小的遗憾。
2.层次化设计