2024春 BUAA OO第二单元总结

刘一鸣-19373454 2024-04-20 18:31:22

第五次作业

题目描述

本次作业的基本目标是模拟多线程实时电梯系统,熟悉线程的创建、运行等基本操作,熟悉多线程程序的设计方法。在本次作业中需要实现六个电梯的建立,并且乘客指定要乘坐某一电梯。

结构图

img

img

结构分析

  • MainClass:主函数,程序入口
  • InputThread:输入线程,从官方包里读取数据
  • OutputThread:输出类,只是把官方包的输出包装一下
  • Request:乘客请求
  • RequestQueue:请求队列,有三种,分别是输入队列,电梯的等待队列和电梯的乘客队列
  • Schedule:调度器,把Input的请求分配给电梯

调度器设计

本次作业乘客指定要乘坐某一电梯,调该预定电梯即可。

第六次作业

题目描述

本次作业指明由哪部电梯来响应,并增加RESET指令和RECEIVE输出。被RESET的电梯需要将接受的请求全部归为未处理状态,然后就近停止更改自己的参数。

调度器设计

采用了随机分配的调度策略,但是需要考虑已维修电梯不能载客的情况,需要对电梯是否维修进行判断,直到寻找到下一个不维修的电梯并向该电梯候乘队列分配请求;线程结束标志也需要进行调整,增加了判断所有乘客是否全部到达目标楼层的标志位,并将其加入到调度线程结束的判断条件中。

LOOK策略

首先,我们为电梯设定一个初始的运行方向(向上),然后电梯开始按照这个方向运行。

当电梯到达某一楼层时,我们需要首先判断是否需要开门:

  • 如果电梯内有乘客到达目的地,那么我们就开门让乘客出去;
  • 如果在这一楼层有乘客想要乘坐电梯,并且乘客的目的地方向与电梯的运行方向相同,那么我们就开门让乘客进来。

接下来,我们需要进一步判断电梯内是否还有乘客。如果电梯内还有乘客,那么电梯就继续按照当前的方向运行到下一楼层。否则,我们需要检查请求队列中是否还有请求:

  • 如果请求队列不为空,并且有请求的发出地在电梯的"前方",那么电梯就继续按照原来的方向运行。
  • 如果请求队列不为空,但所有的请求都在电梯的"后方",或者在当前楼层有请求,但这个请求的目的地在电梯的后方,那么电梯就需要改变运行方向,并重新判断是否需要开门。
  • 如果请求队列为空,并且输入线程还没有结束,那么电梯就停在当前楼层等待新的请求输入。
  • 如果请求队列为空,并且输入线程已经结束,那么电梯的运行就结束了。

Bug分析

在处理线程错误时,我通常会采用一种直接的方法,即在可能出错的地方进行输出,例如轮询。轮询的产生可能是由于在不必要的地方唤醒了正在等待的线程,这导致原本没有任务的、正在休息的线程被强制唤醒,反复检查是否有任务要做,从而增加了CPU的运行时间。另一种可能是线程没有正常进入wait状态。

解决这个问题的方法是通过print来定位产生轮询的线程,然后在每个wait的前后再进行print,以判断线程是否正常进入等待状态。如果线程没有进入等待状态,那就需要检查进入条件哪里出了问题。如果线程已经进入等待状态,但在某个不明确的地方被唤醒,那就需要在notify后加print,以检查是哪个notify唤醒了线程,以及是否可以删除它。

在这次作业中,我们考虑到了最后一个运行中的电梯可能突然需要重置,因此需要有人接手剩余的请求。为此,我们设计了一个较为复杂的逻辑结构,并嵌入了一个数组来存储那些随时可能结束工作的电梯,电梯在准备结束工作时必须自行检查其他电梯是否也准备好了。

第七次作业

题目描述

本次作业新增DBreset指令,将电梯重置为双轿厢电梯。

结构图

 

 在这次的更新中,我们引入了一种新的线程类型和一个共享类。新增的线程类型是双轿厢电梯,而共享类则用于两个双轿厢电梯之间的通信。当出现DCreset指令后,process类将会创建并启动两个作为双轿厢电梯的线程。此时,process类不再作为电梯运行,而是转变为在电梯井内对两个电梯进行局部调度的角色。这是一个重要的改变,它将影响电梯的运行方式和调度策略。

 复杂度分析

本次作业的复杂度分析结果如下:

 

锁的选择

锁的基本作用是解决线程安全问题。当两个线程同时需要访问和修改同一对象时,可能会产生延迟错误。在这个结构中,需要变化的对象包括PersonQueue(乘客队列),ElevatorQueue(电梯队列),Pickup(接人数量)和Server(服务数量)。我将所有的锁都封装到了类中,使得结构更加清晰。通过使用synchronized(),当需要使用对象内的元素时,就需要获取这把锁。这样,其他线程如果想要访问或修改,就需要等待,从而避免了问题的产生。然而,这种方法也会影响效率。在第二次作业中,我添加了过多的锁,导致效率降低。在第三次作业中,在保证线程安全的前提下,我适当地删除了一些锁。

Bug分析

本次作业课下使用测评机时,发现cpu运行时间过长,但是检查run函数也没有轮询存在。最后发现是寻找最短路径的问题,每次寻找路径会试图寻找每一条路径,都要递归非常多次,所以改进算法,只要找到直达就结束。发现是因为在电梯RESET后,等待队列中的其他乘客没有重新规划路线,导致无法到达,最后追加了重新规划。

心得体会

在这单元的作业中,我深入学习并掌握了synchronized同步块的使用,以及线程安全类的实现。要实现线程安全,我们需要先理清逻辑,再进行编程。这样可以避免先编程后再进行逻辑debug的情况。在理清逻辑时,我们需要明确所有线程与所有共享对象的归属问题。在处理同步读写方法时,我们需要考虑哪些方法需要唤醒线程,哪些方法不需要,以避免产生轮询。同时,我们应尽可能减少线程拥有的共享对象数量,尽可能缩小同步块的规模,以避免死锁并提并且我们应该尽可能地对不同的功能进行抽象和封装,减少类之间的耦合度。在进行迭代的过程中,我们应尽量避免修改接口,而应在类的内部进行尽可能多的修改,以免影响其他类的功能。我会在未来的作业中需要注意这些问题,以提高的代码质量和效率。

 

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

301

社区成员

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

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