P1 设计结构

三次作业的架构都没有较大的改动,基本上都是靠调度器接受输入的请求并放入队列,然后调度器根据不同的电梯的当前状态来把请求分配至不同电梯的请求队列中,最后电梯再根据自己的请求队列去运行。因此接下来都以第三次的作业为例来进行讨论。

一、diagram

  • MainClass类

    负责创建并启动调度器线程和所有的电梯线程,然后不断地读取输入并送至调度器。
  • ControllerAll类

    能够读取所有电梯的信息以及所有的输入请求,然后根据电梯的状态来对请求进行分配。其成员变量requests为共享资源数据。
  • Elevator类

    负责保存某个电梯的状态信息。
  • ElevatorThread

    负责保存某个电梯的主请求与副请求队列,并实现了电梯的运行算法。

二、UML框架图

接下来由框架图大致演示一下线程之间的交互

三、设计策略

资源分类

  1. 共享资源

    • ArrayList<PersonRequest> requests

      负责保存全局的乘客请求,然后由调度器去分配这些请求给相应的电梯。由于考虑新请求的产生可能会使得请求分配最优解的变化,因此要使用再分配机制,即给每个电梯的副请求(未进入电梯的乘客请求)分配时,并不会将这些请求从requests总请求中剔除,当且仅当请求从电梯的副请求转移至主请求时(乘客进入电梯),才将requests中对应的请求剔除。
    • ArrayList<Elevator> elevators

      负责为电梯运行部分和任务调度部分提供相应的电梯状态信息。

      任务调度部分主要对电梯状态进行扫描读取,以便衡量如何分配请求。

      电梯运行部分主要是对电梯状态去进行改变。
  2. 非共享资源
    • ArrayList<InElevatorPersonRequest> mainRequest

      保存在电梯运行线程类中,作为电梯的主请求(即目前在电梯内的乘客请求)。通过副请求向主请求的转移(乘客上电梯)增加请求,通过乘客下电梯而删减请求。
    • ArrayList<PersonRequest> secondRequest

      保存在电梯运行线程类中,作为电梯的副请求(想要上该辆电梯的乘客请求)。通过调度器的任务分配而改变请求,以及向主请求中转移而删减请求。
  3. 不变资源

    主要定义在Define类里,由作业要求定义而来,如楼层数、电梯运行速度、电梯最大载客量等。

多线程协同和同步控制

  1. 加锁原则

    由于两个共享资源都有可能被进行读写操作,因此在任何要扫描共享资源或者改写共享资源的类的地方均上锁。对于仅读取或改写一个共享资源的方法添加相应类型的锁;对于要读取或改写两个共享资源的方法,采用先对requests加锁,后对elevator加锁的方式,以避免死锁的产生。

  2. wait和notify控制

    • 用户输入

      若有新的乘客请求,假如requests队列,并notify()调度器线程。

    • 调度器分配

      若请求为空或者给所有电梯分配完一次请求后,进入wait()。

      待读取新的请求或者某个电梯的主副请求队列发生改变时,被notify,然后进行任务分配,notify其它所有的电梯线程。

    • 电梯运行

      若主副请求均为空,进入wait()。

      待调度器分配任务后被notify,然后运行电梯。

调度算法

  1. 调度器请求分配算法

    • 流程图

    • 部分优化特点

      • 采用重分配原则,即通过“乘客挑选最近电梯”、“电梯从选择该电梯的乘客中挑选最近的乘客”两个步骤分配完第一个电梯后,第二个电梯的“乘客选择该电梯”的请求可能会变化(因为可能相较于前一次分配,会把前一次没被电梯选择的请求加入到这个请求队列中)。若不采用这种方式,性能会大大折损,例:

        将请求1,2,3分配给A,B电梯->第一次分配,请求1,2,3均选择电梯A->电梯A选择最近的请求1->由于1,2,3均选择了A,B无请求可挑选
      • 采用直达优先原则

        即纵使一辆电梯离某个请求最近,若其不能满足乘客直达,乘客依然不挑选这辆电梯。
      • 分配请求不超过电梯最大人数要求

        记录当前电梯状态,为其分配副请求直到电梯内人数加副请求数量等于电梯最大人数限制为止。
  2. 电梯运行流程

    • 流程图

    • 部分优化特点:
      • 将开关门独立于需要锁的changePeople函数之外(负责上下人的函数块)。使得此电梯休眠期间别的电梯可以进入需要此锁的函数中。。
      • 换乘乘客楼层选择:

        记乘客当前搭乘电梯为A,其它电梯分别为B1,B2,B3,选择能到达该乘客最终目标楼层的电梯,不妨是B1,B2,分别计算A能到达的楼层与其能到达的楼层的交集,不妨设为Z1,Z2,从Z1,Z2中选择目前离该乘客搭乘电梯当前楼层最近的楼层作为该乘客的换乘楼层,从而确定换乘电梯。(但是不考虑其它电梯的状态的话,这样的换乘方式某些情况还是比较低效)

四、可拓展性

若要拓展的方面不脱离 “用户输入->调度器调度->电梯响应”这样的结构的话,本代码至少不会达到难以修改的地步,因为添加部分代码要求并不会对除了调度算法和电梯运行算法之外的地方产生影响,函数块的耦合度较低(虽然类之间的耦合度很高),而调度函数块要面临着较大部分的改动,因为这部分算法要做到全知全能,才能对电梯进行任务分配,如第二次作业到第三次作业,新增了电梯楼层限制,那么该函数中请求挑选合适电梯的部分就要完全重写。但是由于这几次作业都在上述的结构限制内,因此更改起来还是比较容易的。

倘若突破了上述限制的结构,那扩展性几乎是0。

五、设计结构度量分析

下面放Designite Java的结果(虽然我感觉这部分对我来说非常的没有用处,因为纵使告诉了我哪些地方有问题,我也比较难以在下次的编写中特意避免这些问题,只能起到“责怪我这次代码写的不好”的作用,而真实的启发,即如果实际去避免这些比较少)

方法复杂度:

这里挑选了几个代码行数和圈复杂度较高的方法,然而我看了一下,很大的原因是因为我没将电梯按类型去编写类,导致判断电梯类型的时候要用大量的if-else,但是我感觉这一部分无可厚非,采用这样的方式反而能够更加的直观一些。

依赖关系:

P2 bug分析

  1. 自我bug分析

    虽然强测和互测中均为找的我的bug,但是我还是觉得我程序有些地方的书写很奇怪,感觉许多地方没有产生bug是由一部分的特判强撑着,稳定性很差。

    最让我感觉奇怪的地方就是之前为了优化乘客上下部分而将开关门从这部分提取到函数外面,更改乘客的部分就由原本的整体变成了"判断是否开关门->开门->上下乘客->关门",而判断是否要开关门与上下乘客的部分原本应当是衔接的,这两部分都要保持共享数据requests的不变,这样就可能导致出现由于期间共享requests的改变,导致任务重分配,导致本来应当在此楼接的乘客分配到别的电梯中去的情况,即"判断应当开门->开门->(世界线变动)空操作->关门"的情况。

    不过还好这几次作业并不把无意义的开关门作为错误的点,而导致我能够苟过,而且由于现阶段的电梯特性好像不会出现上述空操作的情况。但是这依然是一个潜在风险,例如电梯的可换乘楼层随时间变化的话,可能就会在变换后导致空操作的产生。

  2. 发现别人程序bug所采用的策略

    使用评测姬挂一天就完事了。由于本人知识与能力有限,不会写评测机程序,因此白嫖别人的评测姬。

    (特此鸣谢:pbnb!)

P3心得体会

这个单元的作业让我理解到了设计架构的重要性,以及向他人学习的重要性。第一单元的作业由于架构的混乱+不吸取他人经验+无脑的上手就开写导致我的作业屡次遭重,而且耗费了大量的时间。但是这次由于参考了某些学长的UML框架图(其实就是架构),以及电梯运行的算法,因此写起来作业感觉轻松多了。

这个单元也让我明白了:

必要与熟练的知识基础+良好的设计架构+提前的较为完整的构思 = 一份轻松且质量较好的代码。

oo第二单元博客总结的更多相关文章

  1. OO第二单元博客

    三次作业的设计策略 第一次作业 多线程协同控制 第一次作业只需要两个线程和一个公共缓冲区: 负责读取输入并把它添加进命令队列的线程,即生产者 负责从命令队列中取出命令执行的线程,即消费者 再加上一个缓 ...

  2. OO第二次博客作业—17373247

    OO第二次博客作业 零.写在前面 OO第二单元宣告结束,在这个单元里自己算是真正对面向对象编程产生了比较深刻的理解,也认识到了一个合理的架构为编程带来的极大的便利. (挂三次评测分数 看出得分接近等差 ...

  3. OO第二次博客作业——电梯调度

    OO第二次博客作业——电梯调度 前言 最近三周,OO课程进入多线程学习阶段,主要通过三次电梯调度作业来学习.从单部电梯的傻瓜式调度到有性能要求的调度到多部电梯的调度,难度逐渐提升,对同学们的要求逐渐变 ...

  4. OO第二次博客作业(第二单元总结)

    在我开始写这次博客作业的时候,窗外响起了希望之花,由此联想到乘坐自己写的电梯FROM-3-TO--1下楼洗澡,然后······ 开个玩笑,这么辣鸡的电梯肯定不会投入实际使用的,何况只是一次作业.还是从 ...

  5. OO第二次博客作业--第二单元总结

    第一次作业 1. 设计策略 第一次作业,一共三个线程,主线程.输入线程和电梯线程,有一个共享对象--调度器(队列). 调度的策略大多集中到了电梯里,调度器反而只剩下一个队列. 2. 基于度量的分析 类 ...

  6. 北航OO(2020)第二单元博客作业

    第二单元第一次作业 多线程设计策略 第一次作业的想法是设计三个线程:输入线程,调度器线程以及电梯线程.输入线程获取请求并发送给调度器线程:调度器线程通过查询电梯线程的状态(等待.停靠以及移动),并综合 ...

  7. 渐入OO课的深处,探索多线程的秘密——OO第二次博客总结

    一次又一次的挑战,一次又一次全新的知识,我来到了多线程的面前 第五次作业 1.度量分析 >第五次作业由于很大程度上调用的是前两次电梯的一些代码,所以存在的问题与前几次也十分相似.同时由于第一次使 ...

  8. 2019年北航OO第二次博客总结

    一.多线程电梯系列作业设计策略 1. 第一次作业——"FAFS傻瓜电梯" 第一次作业是先来先服务的"傻瓜电梯",我当时觉得这个设计未免太简单了,于是就在傻瓜电梯 ...

  9. oo第二次博客-三次电梯调度的总结与反思

    本单元从电梯调度相关问题层层深入,带领我们学习并运用了了多线程相关的知识. 三次电梯调度依次为单电梯单容量.单电梯可携带.多电梯可携带. 一.我的设计 在第一次作业中,使用了最简单的FIFO调度方法. ...

随机推荐

  1. 「NGK每日快讯」2021.1.26日NGK公链第84期官方快讯!

  2. Mosquitto

    mosquitto可连接远程服务器及本地服务器. mosquitto可在一个节点内建立一个连接用于收发,也可在一个节点内建立多个连接用于收发.建立一个连接用于收发时,会有初始部分帧的延迟.(可能由于内 ...

  3. mysql事务的实现原理

    此篇文章算是对mysql事务的一个总结,基本把mysql事务相关的知识点都涵盖到了,面试问来问去无非也就是这些,在了解这些之前我们先对mysql在执行的过程中 有一个整体的认识,如下图 如上图所示,M ...

  4. AtCoder Beginner Contest 192 F - Potion

    题目链接 点我跳转 题目大意 给定 \(N\) 个物品和一个 \(X\) ,第 \(i\) 个物品的重量为 \(ai\),你可以从中选择任意个物品(不能不选) 假定选择了 \(S\) 个物品,物品的总 ...

  5. js获取数字数组最大值的几种方式

    原生Math.max方法 Math.max 方法不能接收数组,可以使用ES6的...将数组打散 const arr = [111, 12, 111, 34, 2, 5, 76]; console.lo ...

  6. Hystrix熔断器的使用步骤

    1.添加熔断器依赖 2.在配置文件中开启熔断器 feign.hystrix.enabled=true 3.写接口的实现类VodFileDegradeFeignClient,在实现类中写如果出错了输出的 ...

  7. http接口的学习(暂时只是记录,测试之后重新更新)

    链接1:https://blog.csdn.net/java173842219/article/details/54020168 链接2:https://blog.csdn.net/u01132074 ...

  8. Java基础学习--集合

    集合 List集合的特点 1.有序的数据集合,存储元素和取出元素的顺序是一致的(存储123,取出123) 2.有索引,包含了一些带索引的方法 3.允许存储重复的元素 List接口中带索引的方法 add ...

  9. 在windows 下查看ip 地址和 在ubundu 下查看IP地址

    在windows 下查看ip 地址和 在ubundu 下查看IP地址 1.在windows 下查看 IP地址:ipconfig 2.在 ubundu 下查看IP地址:ifconfig

  10. PHP题库1

    选择题11. php中,不等运算符是( B.C ) A ≠    B !=   C <>   D >< 2. 函数的参数传递包括:( A.B ) A 按值传递       B ...