面向对象第二单元总结

oo第二单元总结

目录

  • 第五次作业
    基本思路
    UML类图
    UML协作图
  • 第六次作业
    基本思路
    UML类图
    UML协作图
  • 第七次作业
    基本思路
    UML类图
    UML协作图
  • 同步块的设置和锁的选择,并分析锁与同步块中处理语句之间的关系
  • 总结分析三次作业中的调度器设计,并分析调度器如何与程序中的线程进行交互
  • bug分析
  • 发现他人bug的策略
  • 心得体会

第五次作业

  • 基本思路
    • 两个线程——input和elevator线程,由于第五次作业只存在纵向电梯且每座只有一台,于是在Main中新建五个电梯对象即可,并为他们分配其对应的调度表;input处理输入请求,并与五个电梯线程共享调度表对象,分析请求所在楼座,将请求加入对应楼座电梯的调度表中;
    • elevator类负责实现电梯的移动、开关门和接送乘客,循环执行move->open?->(open->in/out)->changestate直至满足线程结束条件。具体实现——
      • 在电梯类中增加一个state属性控制电梯的移动方向,-1为向下,1向上,0为初始状态,2为线程结束状态,根据state的值完成move
      • 是否开门分为是否有乘客到达和调度表中的请求能否在该层上来;
      • 乘客先进后出,注意这里不能从乘客队列开始判断是否出去和移除该乘客,可能导致一些相邻的需要移除的请求被忽略掉;
      • changestate主要是根据调度策略变换电梯下一步的运行方向
        • 对于每一个电梯,都采用 ALS 策略,即新增主请求和被捎带请求两个概念

            1、主请求选择规则:
          
            (1)、如果电梯中没有乘客,将请求队列中到达时间最早的请求作为主请求
          
            (2)、如果电梯中有乘客,将其中到达时间最早的乘客请求作为主请求
          
            2、被捎带请求选择规则:
          
            (1)、电梯的主请求存在
            (2)、该请求投喂的时刻小于等于电梯到达该请求出发楼层关门的截止时间
            (3)、电梯的运行方向和该请求的目标方向一致
          

UML类图
image
UML协作图
image

第六次作业

基本思路

  • 第六次作业相较第五次作业主要变化在两个方面
    • 新增横向电梯,在某一楼层可以在ABCDE环形移动
      • 解决方法:依照纵向电梯新建一个CrossElevator类,只需要改变open?的条件和changestate的策略,总体框架与纵向电梯相同;
    • 可以新增纵向和横向电梯
      • 解决方法:在input中对请求进行分类处理,一类为新增请求,一类为新增电梯(即新建一个电梯线程);
    • 乘客请求可以是同座纵向或是同层横向
      • 解决方法:在input中判断他是横向还是纵向请求将其加入对应的调度表中即可;
    • 一些补充:
      • 由于可以新增电梯,一个楼层or楼座会对应多台电梯,他们可以接受的请求的是相同,于是建立TotalDispatch对应该楼层/楼座全部待分派请求,采用自由竞争的策略分派给该楼层/楼座电梯的SelfDispatch;

UML类图
image

UML协作图
image

第七次作业

基本思路

  • 运用单例模式实现15个总的调度表
  • 处理分段请求
    • 用Flag位标记请求当前能否被相应执行(可能要求前序请求)
    • 对应地在电梯类中,若请求完成,需要查找后续是否有换成请求,如果有将其Flag置1
    • 寻找换乘楼层,每一个电梯其对应的调度表中都有一个属性代表电梯可抵达的开门楼座,如果该调度表对应开关门信息符合要求则参与竞争。

UML类图
image

UML协作图
image

同步块和锁

同步块的设置和锁的选择,并分析锁与同步块中处理语句之间的关系

  • 在本次作业中采用的是java的synchronized机制,实现较为简单。
  • 资源竞争关系
    • Input分发请求时会对总的调度表进行写入操作;
    • 每台电梯都带有一个Distribution,负责分发时需要读该楼层/楼座总的调度表,再有请求需要分发时,会对该楼层/楼座总的调度表以及该电梯自己的调度表(实际是等待队列)进行写操作
    • 电梯检查是否有乘客进来时,需要读该电梯的调度表(实际是等待队列),在 进出时需要对其进行写操作;在处理分段请求时,需要访问对应可能存在换乘的总调度表,对其进行写操作(将换乘的下一个请求Flag置1)
    • 输出线程
  • 为避免轮询,采用wait和notifyAll的方式
    • 在总的调度表为空时,若input为结束,Distribution等待;input放入新的新的请求/input结束时notifyAll;
    • 电梯中,若该电梯无乘客、候乘表为空、Distribution未结束(即调度表isEnd == 0),电梯wait;当调度表有新请求加入或调度表isEnd,notifyAll;
  • 对synchronized机制的理解
    • 对于同一个加锁对象的语句块,每次最多只有一个语句块可以拿到锁,此时可以访问该对象,该语句块结束后释放锁,其余语句块才有可能得到这个对象的锁,从而拥有访问和写入的权利;
    • 修饰块——锁定的是传入的对象
    • 修饰方法——
      • 静态method:锁定类
      • 非静态:锁定方法的调用者

调度器

总结分析三次作业中的调度器设计,并分析调度器如何与程序中的线程进行交互

  • 第一次作业
    • Input线程往对应电梯的调度表中加入输入请求,构成该电梯的等待队列;当电梯线程判断是否有乘客进入以及后续进入操作时需要访问/写调度表,当电梯中没有乘客,为了接调度表中第一个乘客也需要访问调度表;
  • 第二次作业
    • 可能一个楼层/楼座对应多个电梯,为了实现分发请求新建Distribution类,其中每个电梯伴随一个Distribution对象,在这个Distribution对象中有该电梯自身的调度表(作用同作业一中电梯的等待队列)和该楼层/楼座总的调度表;
    • input线程按照请求类型,将请求加入到对应楼层/楼座总的调度表中;Distribution中采用自由竞争策略将请求从总的调度表分发到各个电梯各自的调度表中;
  • 第三次作业
    • 在前两次的基础上,将各个楼层/楼座总的调度表集合成一个类,采用单例模式实现一次;
    • input将请求分段后加入对应的楼层/楼座总的调度表中;Distribution中仍采用自由竞争策略将请求从总的调度表分发到各个电梯各自的调度表中,但此时需要注意横向电梯可到达楼座不相同,分发时还需考虑该电梯能否运送请求;
    • 在电梯中有乘客到达时,需要访问总的调度表中是否有该请求的后续换成请求,若有则Flag位置1;

bug分析

  • 开始没有理解“输出线程不安全”这一点,导致输出时间戳非递增
    • 新建了一个Output类,采用ynchronized锁定输出方法,用这个类实现输出;
  • 从调度表中接收乘客时,采用了从0-end的遍历方式,符合条件则从调度表中删除请求,导致其后一个请求在遍历时被忽略
    • 从end-1到0进行遍历,满足条件则从调度表中移除加入电梯乘客序列
  • 存在很多实际没有用到的notifyAll,虽然在本地运行时不会报错,但可能会导致ctle
    • 删除不必要的notifAll
  • 在横向电梯接乘客时,缺少了在本楼层的判断条件而知判断了方向一致的条件
    • 添加条件即可
  • 同楼层的横向请求可能由于电梯的二开关门信息限制也需要换成
    • 加上这一判断条件即可

发现他人bug的策略

  • 遍历请求类型(尤其是特殊请求),检查输出线程是否安全,检查处理时间是否符合要求
    因为多线程运行的复杂性,与第一单元相比,这次作业bug的判断困难很多。

心得体会

  • 本单元作业是我第一次接触多线程编程,通过老师的课上讲解以及在作业中逐步摸索,让我初步了解了多线程编程的思想与一些使用方法,收获颇丰。同时在这次作业中由于没有做好充分的测试,也出现了很多诸如基本判断条件缺失这样十分不应该出现的bug。
    回过头来看,经过第二单元的洗礼,我对于面向对象编程思想的了解进一步加深,也初步了解了多线程编程的思想,在完成作业的过程中也锻炼了我对于整体框架思考和构建的能力,继续加油吧!
posted @ 2022-05-01 17:00  摘星771  阅读(65)  评论(1编辑  收藏  举报