BUAA OO第二单元总结

全伟业-22371393 学生 2024-04-20 00:50:27

面向对象课程第二单元总结

前言

OO第二单元的主题是电梯调度,考察的重点在于多线程,其中面对共享数据的线程交互以及线程安全问题,对于首次接触来说有很大难度。

第五次作业

任务分析

本次任务需要通过电梯接送乘客,每个乘客都被分配好了上哪一个电梯,由于都已分配好了,本次作业也就不涉及乘客上哪座电梯的分配策略,本次作业相对简单

代码UML类图

img

架构分析

本次一共开启了8个线程,除了6个电梯线程外,有一个输入线程和一个输入调度的线程

生产者-消费者模式

输入架构采取了生产者-消费者模式:

  • 生产者:输入线程(InputThread),从终端获取乘客信息
  • 消费者:输入调度线程(Scheduler),电梯线程(Elevator),输入线程的数据存入WaitTable(共享数据)类(定义一个总候程表WaitQueue),然后调度线程将用户存到对应电梯的候程表,电梯线程来完成用户的请求。

同步块的设置和锁的选择

本次作业的共享数据是WaitTable这个类,其中对于总候程表,输入线程和输入调度线程有竞争关系,对于每个电梯的候程表,每个电梯和输入调度线程有竞争关系,在对这类共享对象读写时,要上锁,保证同一时间只有一个线程读写访问

策略

本单元作业采用LOOK策略,对电梯的乘客和等候的乘客进行捎带等功能,LOOK策略更加稳定。

策略类的设置(Strategy类)

为什么要设置这个类?目的是将电梯的移动、开关门等动作变成一个动作,只要完成一个动作就释放锁,提高Scheduler类的竞争能力,否则如果一次性执行很多动作再释放锁,电梯线程会占用太多,输入跟不上,最后会导致RTLE
里面具体实现了判断是否要开关门的方法,是否掉头的方法,并有一个最终建议的反馈

代码复杂度分析

img

第六次作业

任务分析

本次作业与上一次相比更新了两点:

  • 输入不再为乘客分配电梯,由我们自行决定乘客的分配
  • 新增reset操作,将电梯属性重置,并且在重置过程中要清空电梯及其候程表

代码UML类图

img

架构分析

  • 新增了Person类(其实是上一次作业直接用了jar包写的PersonRequest请求,这次由于resetRequset的类的更新不得不新增)

  • 需要进行对乘客去往哪个电梯的分配,有影子电梯、随机分配、均匀分配等方法,这里采用的是随机分配,处于对reset的考虑,其他方法都只是局部上的最优解,未必性能会更好,故方便下一次迭代的改动选择了随机分配

同步块的设置和锁的选择

和上一次作业迭代的改变处在于,由于reset要将候程表和电梯内乘客移除到整体候程表,所以全局候程表也成为电梯可享用的资源,在电梯线程reset时也需要对其上锁

策略改动

reset其实本质上也属于电梯的一个动作,和上下行开关门等操作是并列的关系,在策略提供建议时要判断何时reset,如果电梯内有乘客并且在移动两次内可以到目的地,则移动一次或两次后让这些乘客到目的地再进行resset

代码复杂度分析

img

bug修复

本次强测挂掉没进互测,原因是一处迭代时,电梯初始容量为6,reset后可能发生改变,忘记修改在让乘客进入电梯时,电梯最多容纳乘客数的修改,相较之下只改动了一下变量名,很可惜

第七次作业

任务分析

本次作业新增了双轿厢电梯,也就是第二种的reset请求,一个电梯井在该种请求下变成两个分区,两个电梯同时运行,难点在于如何进行新的线程交互和保证线程安全,以及如何避免两个电梯相撞

代码UML类图

img

架构分析

  • 第二种reset的时机和第一种一样,并没有区别,唯一有区别的是对于双轿厢电梯的处理
  • 因此在电梯线程类新增双轿厢reset方法,在这个方法中新开启线程,将原线程变为A轿厢,新建B轿厢并更新参数
  • 对于receive,在Scheduler线程中进行判断,是位于哪个双轿厢中,不难发现根据LOOK策略,每个乘客位于该电梯井被A、B哪个轿厢receive是容易判断的(只需根据起始、目的地和换乘楼层三个的位置关系判断即可)

防止相撞

新建一个TransFlag类,AB轿厢共享一个该类的对象,用于判定哪座电梯占用了换乘楼层,对该对象上锁,因此就保证了同一时间两个轿厢不会同时处于该楼层,比较轻易的解决了问题

同步块的设置和锁的选择

本次作业只新增了TransFlag类里的锁

代码复杂度分析

img

hack策略

将6个电梯全部置reset,然后接待乘客,会使一些没有处理好reset操作的人的代码超时,成功hack了3个

Debug方法

多线程的debug很难复现,因为每次跑完都是不同的结果,因此在一次评测出问题,就应该去查找是哪里的问题,把bug锁定在某一个区域,某一个方向。
很多时候会出现线程无法结束,一直处在阻塞状态没有被唤醒,使程序无法结束,此时可以通过去输出各个线程状态去判断哪里的问题,如何去处理。

心得体会

不能随意乱加锁,不加锁会出现线程安全问题,加多了锁会让竞争偏移,某一方过强导致线程之间不能协调导致超时;迭代时要仔细检查参数的迭代,第六次作业因为该问题没进互测非常遗憾

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

301

社区成员

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

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