OO第二单元博客

OO第二单元博客

第一次作业

1 整体架构

​ 本次作业的UML图如下所示:

2 需求容器设计

​ 本次作业中,每个楼座都由一个对应的RequestTable。每个RequestTable中,按照出发楼层将需求分别存放在10个队列中,这10个队列则由一个HashMap存储,其中Key是楼层,Value是队列。

3 调度器设计

​ 我在设计时并未实现专门的调度器,而是直接将调度策略实现在了Elevator的run()方法中。因为个人认为本次作业中调度器没有任何必要,反而使代码显得臃肿。我的调度策略与非常流行的Look策略大致相同,在强测中也取得了不错的性能表现,具体如下:

目的楼层的确定
  • 若电梯内有乘客:以电梯内乘客当前方向上的最远楼层为目的楼层
  • 若电梯内没有乘客:
    • 若当前方向上的楼层有等待中乘客:以当前方向上最远的乘客所在楼层为目标楼层
    • 若当前方向上的楼层没有等待中乘客:转向,并按照上述方式确定目标楼层
捎带

​ 电梯前往目标楼层的过程中,对途径楼层中目标方向与电梯运行方向相同的乘客进行捎带

等待

​ 当且仅当满足以下三个条件,电梯进入等待:

  • 电梯内没有乘客
  • 当前楼座RequestTable为空
  • 输入未结束

4 线程交互

​ 采用生产者-消费者模式,以Input为生产者、Elevator为消费者,二者通过共享对象RequestTable进行交互。

5 线程同步

​ 采用了非常简单粗暴的同步方式:

  • 对共享对象RequestTable的每一种方法加synchronized
  • 每个方法结束时notifyAll()

6 Bug

  • 强测:参与强测的版本中,由于在捎带时未考虑乘客的目标方向,导致了RTLE。
  • 互测:未出现Bug,也未发现他人Bug。

第二次作业

1 整体架构

​ 本次作业的UML图如下所示:

2 需求容器设计

​ 本次作业中,用一个RequestTables类统一管理所有的RequestTable。每个楼座对应一个RequestTable,与上一次不同的是,每个RequestTable中包含2个HashMap,分别对应上行和下行的需求,HashMap与第一次设计相同。横向电梯的设计类似,每个楼层同样对应一个RequestTable,不同的是每个出发楼座对应一个HashMap,HashMap的Key为目的楼座。

纵向电梯RequestTable的结构为:

方向\出发楼层 1 2 ... 10
上行
下行

横向电梯RequestTable结构为:

出发楼座\目的楼座 A ... E
A
...
E

3 调度器设计

  • 单个电梯:纵向电梯调度策略与第一次相同,横向电梯也只是在此基础上对环形的情况稍作调整,不再赘述
  • 多个电梯:多个电梯之间采用自由竞争的策略,即不专门为电梯分配任务,先到先得。好处是实现简单、不易出错,坏处是可能导致电梯“白跑一趟”的情况,导致浪费。该策略在强测中性能表现较好。

4 线程交互

​ 依然采用生产者-消费者模式,整体上与上一次相同。对于新增的多个电梯的情况,同一楼座或楼层的电梯共享同一RequestTable。

5 线程同步

​ 与上一次相同。

6 Bug

​ 本次作业在强测和互测中未出现Bug,也未发现他人Bug。

第三次作业

1 整体架构

​ 本次作业的UML图如下所示:

2 需求容器设计

​ 与第二次作业结构相同,只是将存放的对象由PersonRequest改为了自定义的乘客类Passenger。使用乘客类的目的是分解乘客的需求,从而方便电梯进行处理。分解规则如下:

  • 若出发楼座与终点楼座相同,或出发楼层与目的楼层相同且存在可用(即在出发楼座和目的楼座上都可停靠)的横向电梯,则不进行分解;
  • 若出发楼层有可用的横向电梯,则分解为FromFloor上的FromBuilding→ToBuilding和Tobuilding上的FromFloor→Tofloor两个需求;
  • 若终点楼层有可用的横向电梯,则分解为FromBuilding上的FromFloor→ToFloor和ToFloor上的FromBuilding→ToBuilding两个需求;
  • 若不满足以上条件,则先找到最近的可用横向电梯并记其楼层为TransFloor,将需求分解为以下三个子需求:FromBuilding上的FromFloor→TransFloor、TransFloor上的FromBuilding→ToBuilding以及ToBuilding上的TransFloor→ToFloor。

3 调度器设计

​ 与第二次作业相同。

4 线程交互

​ 与第二次作业相同。

5 线程同步

​ 与第二次作业相同。

6 Bug

​ 横向电梯等待条件设计不合理导致轮询,出现CTLE。具体而言,我所设置的等待条件是当层没有等待中乘客且电梯内没有乘客,那么假设某层只有一个乘客,该乘客的需求是A→B,若1号电梯可以在A、B开门,而2号电梯不可以,那么在1号处理完成该需求的过程中,2号电梯既不能处理该需求,又不能进入wait(),而是会不断地判断是否可以等待,即轮询,从而导致了CTLE。

​ 互测中发现了一些他人的Bug,主要包括线程未正常结束而导致的RTLE、同步不当导致异常等。

UML协作图

​ 这里给出第三次作业的UML协作图:

心得体会

​ 本单元的作业是我第一次接触多线程编程。通过这次作业,我进一步加深了对多线程的理解,并初步掌握了多线程编程的要点,理解了线程同步、线程安全等问题,也增加了多线程调试的经验。

​ 线程安全方面,我在本单元的作业中并没有遇到线程不安全的问题,应该是因为我采用的设计比较保守且简单粗暴。层次化设计方面,个人的经验主要是在需求容器的设计上,将需求进行恰到好处的分解,的确可以极大地方便电梯的调度,而不合理的容器设计则会给调度带来很大的麻烦。调度策略上,如荣老师课上所说,不存在所谓最优的调度策略,合理即可。而我采用的非常简单的Look+自由竞争策略在强测中也有着还不错的性能表现。

posted @ 2022-05-03 11:11  h_bh  阅读(14)  评论(0编辑  收藏  举报