oooo第二单元总结

王书勤-22373507 学生 2024-04-20 15:30:10

同步块与锁

可以选择对方法上锁,也可以选择对对象上锁。这里本人采用的是对方法上锁,在调用RequestTable对象的任意读写方法都用synchronized上锁,保证最多只有一个线程访问或修改读写队列。其实这个在第二次实验之后发现可以改成读写锁,但并不知道如何封装。原有的对RequestTable加锁方法对外是透明的,在其他地方不需要过多地考虑锁和同步问题,于是没改。

调度器设计

需要设计的策略细分下来应该有两种,一个是调度器如何将请求分配给电梯,另一个是电梯面对分配给自己的请求采用怎样的方式处理。

先说第二个,这个是沿用往届的LOOK算法,具体实现如下:

  • 首先为电梯规定一个初始方向,然后电梯开始沿着该方向运动。
  • 到达某楼层时,首先判断是否需要开门
    • 如果发现电梯里有人可以出电梯(到达目的地),则开门让乘客出去;
    • 如果发现该楼层中有人想上电梯,并且目的地方向和电梯方向相同,则开门让这个乘客进入。
  • 接下来,进一步判断电梯里是否有人。如果电梯里还有人,则沿着当前方向移动到下一层。否则,检查请求队列中是否还有请求(目前其他楼层是否有乘客想要进电梯)——
    • 如果请求队列不为空,且某请求的发出地是电梯"前方"的某楼层,则电梯继续沿着原来的方向运动。
    • 如果请求队列不为空,且所有请求的发出地都在电梯"后方"的楼层上,或者是在该楼层有请求但是这个请求的目的地在电梯后方(因为电梯不会开门接反方向的请求),则电梯掉头并进入"判断是否需要开门"的步骤(循环实现)。
    • 如果请求队列为空,且输入线程没有结束(即没有输入文件结束符),则电梯停在该楼层等待请求输入(wait)。
    • 如果请求队列为空,且输入线程已经结束,则电梯线程结束。

(摘自Hyggge‘s Blog调度策略

第一个调度器分配,其实根据题目不同会有不同的“最优”调度方法。本人调度器分配方式从调参变到均分,尝试影子电梯未遂。

第一次作业

第一次作业中,由于题目非常贴心地给出了电梯id,所以不存在调度器的说法,直接由InputHandler交给电梯即可。

UML类图

img

时序图

img

这里RequestTable又是ArrayList又是HashMap是因为一开始用ArrayList发现RTLE了,以为是数据结构问题于是改成:处理总队列的时候按照ArrayList先进先出,在电梯处理的时候按照HashMap,人在外面则以FromFloor作索引,人在电梯里则以ToFloor为索引。

bug

  • RTLE实际上并不是数据结构的问题,而是策略问题导致电梯在同一个楼层重复开门关门开门关门
  • 一开始由于对多线程理解不够深刻导致出现很多次轮询……
  • 本人自以为有效(实际完全错误)地修改了电梯策略,如果电梯是空的就不检查人的方向和电梯方向是否一致,把电梯方向改成人的方向。这样电梯跑一趟可能不能把所有人给接上,加上等待的人是用HashMap忽略了等待时间这一衡量因素,导致可能会让先来的人等很久才能被接到,所以很简单的第一次作业性能分奇低无比被分到了B房……

第二次作业

第二次作业意料之中地取消了宝宝椅,请求到来时不给指定电梯id,并且意料之外地ban掉了自由竞争,电梯必须输出Receive有明确的目标才能移动。

架构设计

本人在该作业中采用调参策略,简单来讲就是衡量各个电梯当前所在的位置、方向和容量进行评估,分配给得分最高的电梯。

主要修改及新增:

  • RequestTable数据结构仅保留ArrayList。
  • 增加Person类,电梯接到Reset指令后会把人放出,此时修改Person的FromFloor属性
  • 增加Scheduler类进行调度
  • Main中增加对电梯列表的管理

UML类图

img

时序图

img

bug

  • 强测没有什么问题但在互测中被狠狠攻击了:如果在初始同时给大量相同请求,由于本人设计中未考虑电梯待处理队列的数目导致所有电梯得分会相同,默认分给一号电梯导致被挤爆。
  • 本人的调参电梯并没有设计过多的安全保护,可能会出现调度器查询时电梯在x楼,但实际分配时电梯已经跑了的情况。当时写的时候觉得不会出现错误,只是会对性能造成影响。再加上这种情况发生概率相对较低,于是没有进行过多处理。实际上为了安全保护,可以新增一个电梯信息类,让电梯和调度器访问和修改的操作都是不可中断的。然而电梯本身会需要查询很多次且修改很多次属性,这样设计似乎又有些愚蠢。故也放弃了。
  • 哦不对强测也有问题,没能保证先输出OUT再分配给其他电梯所以出现了WA。

第三次作业

第三次作业主要新增的就是将普通电梯改为双轿厢电梯。

架构设计

本人在这次作业采用了非常神奇(yuchun)的设计,即如果电梯类收到了双轿厢重置请求,则该电梯执行调度器的功能,管理两个subElevator。这样设计的好处是对于以前的调度器而言,电梯数量始终为6,分配给双轿厢的处理方式对外不可见,起到了很好的封装作用。

subElevator和普通的电梯区别主要在:

  • 无需考虑Reset指令
  • 移动到换层楼层需要检查伙伴电梯是否在换层楼层
  • 若在换层楼层电梯阶段性结束,即等待新处理队列,则需要及时离开换层楼层。

上层的逻辑基本没变,并且双轿厢电梯把人放到换层楼层后并不是扔回大请求队列,而是直接由电梯调度器给伙伴电梯,结束判断条件也只需要从电梯开始调整,而不需要动上层框架(优雅但不是很优雅)。

UML类图

img

时序图

img

bug

这次作业没啥bug,毕竟均分的特点就是牺牲一些性能分,换来绝对的安全。

但是从上一次的惨败中吸取了教训:sunglasses:掌握了hack技巧:

  • 同时Reset所有电梯,同时分配大量请求
  • 同时Reset五个电梯,留一个电梯不管(或者提前很久Reset),同时分配大量请求
  • 同时普通Reset五个再DCreset一个
  • 同时DCreset五个再Reset一个
  • 单给一个Reset指令

双轿厢

双轿厢防撞车参考了讨论区的分享,设置一个Flag类管理换乘层是否被占用。每个两个电梯共享一个Flag Occupied,但看讨论区发现不对这好像就是锁……所以其实不用自己写……?

心得体会

三次作业成绩太精彩了,第一次自以为有效的优化实际完全错误导致性能分极低(在大家都很高的情况下),第二次终于选择照顾性能分了但又WA了一个点导致分还是低且在互测被狠狠攻击,第三次遂摆烂选择牺牲性能分换取正确性。

一直采用的防御性编程导致似乎没有出现很多线性安全问题,倒是保证输出顺序正确以及正确且正常结束这两个部分让人很伤神。

感觉整体架构还挺清晰的所以每次新增功能也没有更改很多代码(不像第一单元重构重构重构……)而且双轿厢的设计感觉比往届的横向电梯更合理一些,新增功能增加得很开心。

但乘客能不能再灵活一些啊!都只有一层楼了就不能自己走吗!?

...全文
58 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧