2024面向对象第2单元博客

姚凯-22373239 学生 2024-04-20 13:48:43

架构设计

最终的sequence diagram如下,这部分的总体框架,即生产者-消费者的总模式在全单元没有大的改动。

第一次作业

 第一次作业架构UML类图如下

初步涉猎多线程采用两层生产者-消费者模式。为了降低耦合度,将电梯与策略类分开,一定程度上也为未来工厂模式组装不同电梯做准备(虽然并没有)。

这边的waitQueue表示总请求队列,waiters与processingQueue表示各电梯的请求队列。

同步块与锁

第一次作业中在没有乘客请求调度优化的情况下,整体架构为两层生产者-消费者模式:输入类与调度器类分别就总请求队列成为生产者与消费者,调度器类与各电梯运行类分别就电梯请求队列成为生产者与消费者。其中,总请求队列与各电梯请求队列成为锁。在消费者与生产者进行消费生产时设置同步块。

出现的BUG

第一次作为初见多线程,对于bug的出现,线程的管理首当其冲。常常会出现Schedule抢不到各电梯请求队列,导致电梯的look策略不能发挥较强的效果。debug的时候就应严格控制同步块的范围,确保对共享资源的及时更改。同时对于同时多请求的情况,可以让schedule在sleep小时间后统一处理之前的同时间多请求。

稳定和易变的内容

第一次作业定下的生产者-消费者模式是最为稳定的内容,而schedule在请求的调度与电梯的属性上,第一次作业并没有涉及,为易变内容。

第二次作业与第三次作业

第三次作业的UML类图如下,个人将电梯与电梯运行进行了解耦,也便于电梯作为共享资源参与线程控制。同时第一次作业用官方jar包中的person request类现在为了能够修改其出发楼层个人新建了passenger类。

 这边的passengers与waitQueue表示总请求队列,waiters与processingQueue表示各电梯的请求队列。

同步块与锁

第二次作业和第三次作业中加入重置指令,此时电梯中的乘客请求将从电梯请求队列中打回并回到总请求队列,即电梯在某种意义上也成为了生产者向总请求队列托盘中添加资源。而电梯的部分属性将在重置中受到修改,抑或是在调度优化中需要被调度器访问属性,电梯本身也将成为共享资源,在电梯运行类与调度器中设置同步块。

调度器设计

第一次作业调度器功能并不强大,只是确认请求指向的电梯id号并将其分发给电梯。

第二、三次作业变化较大。调度器与电梯运行类共享电梯,调度器以此确认电梯是否处于重置状态,同时也便于访问电梯属性来实现权重调度,在第三次作业中还需访问电梯是否为双轿厢电梯来分配到对应轿厢的请求队列。在第三次作业中调度器线程不再是等待完全部重置和没有新请求后就先于电梯线程结束了,而是考虑到双轿厢的换乘,要在所有请求都完成后在电梯的通知下结束。

关于调度策略,在第二次作业为了正确性上的考虑,个人最后回滚版本选择了随机分配,在性能上虽不稳定但对于各种情况也不常出现最糟糕的判断,是简单性价比较高的选择。

如何防止轿厢相撞

为防止轿厢相撞,个人没有新建两个轿厢进程的通信类,而是利用电梯类。两个轿厢线程共享两个电梯实例,在其中一轿厢欲进入换乘楼层时需先查询另一轿厢电梯的属性,来确认换乘楼层空闲。若不空闲则进入等待另一轿厢电梯发出notify。

出现的BUG

第二三次作业出现的主要bug为死锁问题。对此需要再debug过程中多次运用输出线程状态的提示信息来把握运行状态,理清本次运行中的各线程运行状况。在线程wait前后分别println("wait")和println("wake")来作为提示,便于理解是哪几位关于哪个锁出现了死锁。这里死锁的情况多种多样,往往与多个类中关于某一实现的问题有关,应具体情况具体分析。

稳定和易变的内容

第二三次作业中电梯的运行策略和调度器的调度策略上留有可拓展的空间,同时不同类型的电梯也是一易变内容,可在后续考虑应用工厂模式设计不同的电梯。

心得体会

多线程中对共享资源的控制最好在方法内有层次化的实现,而不是临时搭建控制,待到最后代码冗长难堪。锁的进出设计尤为重要,当一个同步块叠加另一同步块时就需要注意互锁问题。

让各线程各司其职,线程与锁做出区分,划分层次界限,这样才能写出简洁适用的好代码。

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

301

社区成员

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

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