OO 第二单元总结

罗皓天-21371229 学生 2023-04-13 20:19:51

OO 第二单元总结

同步块与锁

​ 这个标题有迎合作业格式的意味,本人更愿意理解为如何在多线程编程进行同步控制。第二单元总共使用了四种方法来控制同步,分别是同步块、阻塞队列、信号量、类似 CAS 的操作。实际上,同步块和阻塞队列的也需要锁来实现。

  • 同步块

    • 同步块利用锁的机制实现互斥同步,能够保证当前代码块中的线程安全

    对于本单元的同步控制,同步块的作用主要体现在保护共享变量,避免多线程引发的数据竞争。对于同步块的大小,过大可能会使多个线程不要的等待,过小会造成逻辑上的缺陷。需要注意的是同步块内部不要涉及 Thread.sleep() 的操作,这会造成线程拿着锁进行等待,严重阻碍其他线程的运行。同步块上锁的变量自然需要在同步块中使用,尽量减少嵌套多层同步块的使用,如果出现过多层的嵌套首先应该考虑是否是架构的缺陷,然后考虑一次性获取多个资源,避免潜在的死锁问题。

  • 阻塞队列

    • 采用阻塞队列实现输入与控制器、控制器与电梯之间的生产者消费者模型

    对于乘客的传递,为控制器设置一个总等待队列和为每个电梯设置一个单独的等待队列,这样的机制保证了在系统没有停止运行时的任何时刻向控制器或者电梯新增乘客,不需要额外的同步操作来通知接收方。接收方只需要从队首取出元素,没有元素则等待,这些操作都已经由 BlockingQueue 接口定义好,无需我们手动实现,一定程度上减少了 bug 的发生。

  • 信号量

    • 信号量是一种基础的同步原语, 当 S=1 时即为互斥锁, S>1 时能实现更多的同步操作。

    第七次作业新增的服务中、只接人的电梯就是为信号量量身定制的需求。为每层分配两种信号量,初值分别为 MxNx,在开门前电梯就可以根据电梯内部维护的乘客队列得知是否有乘客下电梯,获取相应的信号量即可。对于特殊的情况,比如在电梯开关门状态切换时发生 maintain 等操作,下电梯的人肯定是增加的,也就是说服务中的电梯可能变成服务中、只接人的电梯,而服务中、只接人的电梯不可能变成服务中的电梯,前者只需要保证开关门前后获取和释放的资源一致,后者是最需要担心的情况但是并不会出现。

  • CAS

    • CAS(Compare And Swap) 假设没有冲突,完成某项操作,完成后判断是否有冲突,若有则重试,直到成功完成。在高并发条件下,这个重试的操作可能会相当占用 CPU 时间,但是在并发量不高的情况下,一个不错的非阻塞的同步方式。

    在本人的设计中,控制器的调度一个人需要获取电梯组的信息,需要获取若将乘客分别分配给每个电梯后该电梯的最终运行时间,如果都放在同一个同步块中,难免会导致同步块过大的情况,实际上,计算过程是很迅速的,而维护、加电梯的指令数量很少(就算全是也就几百条),于是乎,控制器在获取到乘客后,记录当前电梯组状态,计算将该乘客分配给哪个电梯,在向该电梯分配乘客时判断电梯组状态是否改变,即判断是否有增减电梯的操作,若有则重试,没有则向该电梯分配该乘客。

调度器设计

  • 调度器 = 控制器
  • 单例模式

时序图-线程间的交互方式

img

  • 对于乘客的管理,控制器并直接与其他线程交互,而是通过总等待队列获取乘客,然后通过计算将乘客加入某个电梯的等待队列。这样的设计保证了任何时刻只有每个乘客有且只有一个,同时也避免了线程之间的同步,即不需要管其他线程的状态,做好自己的工作即可。
  • Elevator 的静态方法视作电梯组的方法,其他方法视作每个电梯各自的方法,通过 mainTainRequestelevatorRequest 向电梯组发送命令,电梯组再通知控制器重新规划静态策略 remap

迭代中的调度策略

电梯自调度

  • 将分配给电梯的乘客送到指定楼层
  • 在合适时间停止运行
  • 支持在任何合理的时刻加入新的乘客
  • 响应 maintain 指令
  • 响应 add elevator 指令
  • 服务中、只接人

​ 对于前两条,问题简化为将已分配的乘客送至目的地。电梯使用状态机模型,根据已规划的路径运行。根据已分配的乘客规划路径,产生一个乘客队列,电梯只需向队首乘客的目的地运行,到达目的地后开门进出人即可。这样的运行策略相当简单,且每次运行时的行为只跟当前状态和当前乘客队列有关,与上次状态以及上次状态的乘客队列都没有关系,可以很方便的在切换状态时修改乘客队列。

  • 对于电梯外的乘客, 目的地为乘客当前所在层
  • 对于电梯内的乘客, 目的地为乘客的需要去的楼层

​ 对于前三条,即第五次作业的要求,新分配的乘客加入一个线程安全的队列 waitQueue 中, 电梯在每次状态切换后判断有无新分配的乘客,有则重新规划路径,生成一个新的乘客队列(贪心算法)。在不超过载客量的情况下前往与当前所在层最近的乘客的目的地。

​ 对于四、五条,即第六次作业要求,电梯在每次状态切换后判断是否 maintain, 若有则将所有电梯外的乘客直接丢回控制器中的总等待队列, 判断是否处于开门状态后, 把电梯中的乘客丢出去。一旦有 add elevator 指令, 则将所有现存电梯的 reorder 标志设置为 true, 电梯在每次状态切换后判断是否 reorder, 若有则将所有电梯外的乘客直接丢回控制器中的总请求队列。避免新电梯饥饿的情况。

​ 对于第六条,即第七次作业要求,由于知道当前层是否有乘客出电梯, 直接使用信号量实现。特殊情况已经在信号量部分说明,不再赘述。

​ 对于更多的需求,在每次状态切换时可以进行判断。

img

控制器调度

  • 从总请求队列中取出一个乘客并选择一个电梯分配

    • 考虑该乘客分别分配给每个电梯后系统的总运行时间, 取最短时间
  • 处理计算出分配的电梯后电梯没了的情况

    • 类似 CAS(Compare and Swap) 的操作, 判断计算前后是否有电梯的增减, 若有则重新分配该乘客

img

  • 乘客路径规划

    • 第七次作业新增需求
    • 采用静态策略调度, 策略只在电梯增减后更新, 对于给出的出发楼层和终点, 返回一个目的地
    • Floyd 算法:
      求两层之间的最短换乘次数, 最短换乘次数时同时更新目的地
    • 规划完成后, 对于出发楼层 i 和终点 j, 乘客的需要前往的楼层为 map[i][j]
    • 对于第七次作业增加可达性限制, 控制器在增减电梯后更新静态策略, 根据乘客起点 i 和终点 j 计算需要前往的楼层 k, 此时若将乘客的终点视为 k, 则分配该乘客的分配策略其实与前两次作业没有变化(只在能到达 i, k 层的电梯中计算)

性能分析

  • 第五次作业的控制器的调度策略摆烂,强测得分 95.4028
  • 第六、七次作业的电梯策略未变更,控制器策略更换为上述策略,强测得分 98.953399.0926

运行时间

​ 虽然贪心看起来是个很怪异的算法,但是实际运用上不不输其他算法。可以考虑这样一个情形,对于给定的乘客,让一个电梯将他们送到目的地,ALSLOOK 的原则都是回朝一个方向走,捎带乘客,然后到达后再转向继续接送乘客,而贪心其实在开始时会有小范围的波动,即上下小范围运动,但是当这个范围没有目的地后,电梯有很大的趋势向着同一个方向运动,之后的行为与前两个算法相似。对于某些情况,反而具有一定的灵活性。此外,贪心计算出乘客序列后可以得知该电梯还需要的运行时间,这为控制器提供了调度的依据。

等待时间

​ 使用贪心不可避免的会导致乘客饥饿的等待的情况出现,控制器的调度在一定程度上缓解了这种情况,因为控制器的贪心策略要求总电梯运行时间最短,尽量让乘客的需求得到响应。在 maintainadd elevator 的设计上,向控制器的总等待队列插入的乘客位于队首,而从输入的插入的的乘客位于队尾,优先分配先到乘客。

电量

根本没管

整体架构

类图

img

  • 三次作业中架构基本没有变化,第七次作业中为了实现服务中、只接人的电梯新增 Floor 枚举类
  • 每次的新需求基本都是在相关线程的主要逻辑中新增逻辑,因此减少每个需求之间的依赖是相当重要的事,避免牵一发而动全身的尴尬情况出现。

Bug 分析

  • 三次作业中没有被 hack
  • 在使用大量随机数据测试同房时发现有 bug,但是难以复现,三次作业 hack 人数为 010

Bug 类型以及 debug 方法

死锁

​ 程序基本没有出现过死锁的情况,由于同步块基本没有嵌套,唯一可能导致死锁的地方只有结束指令没有及时被响应,导致线程等待,不结束。在线程死锁后使用 jconsole 连接对应线程可以看到详细信息,从而进行调试。

输出错误

​ 输出和状态改变的先后顺序可能导致输出顺序的改变,虽然概率相当小,在本地测试的几万组数据中出现过一例,但是依然应该引起重视,注意先输出再改变状态,避免状态修改后输出前产生其他输出导致的问题。

其他

  • 不要假设任何没有处于同步块中代码会连续执行
  • 对于没有在同步块中的共享变量不能假设其值是不变的

心得体会

​ 虽然此前接触过 GPU 上的并行编程,但是要实现本单元的需求还是需要相当多的时间,多线程情景下有很多同步的策略,在一些地方可能还有更好的实现方式。本单元没有被 hack 过一次,得益于为本单元编写的多线程评测机,使用十几万的随机数据来测试程序的正确性,在很大程度上保证了最后提交程序的正确性。同时使用的贪心策略也取得了相当不错的成绩。在迭代方面,也免去了重构的麻烦,结构可以很好的适应每次新增需求甚至评测机的架构也能很好的迭代

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

442

社区成员

发帖
与我相关
我的任务
社区描述
2023年北京航空航天大学《面向对象设计与构造》课程博客
java 高校 北京·海淀区
社区管理员
  • 被Taylor淹没的一条鱼
  • 0逝者如斯夫0
  • Mr.Lin30
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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