OO2021 - 第二单元总结
2021面向对象设计与结构 - 第二单元(电梯)
序言
第二单元的作业可以说是经历了各种起起伏伏。
第一次作业由于对同步和锁没有足够的了解,因此使用了WaitQueue的方法来解题,加上轮询,导致CPU TLE的结果,未完成。
由于不想再有无效作业,于是我决定在第二次作业发布之前把同步块和锁的原理都了解清楚。这个决定对我之后的两次作业都有不小的帮助。由于了解了原理,加上写代码之前有思考代码结构,因此第二次作业代码写起来可以说是蛮顺利的。完成第二次作业也给了我更多的自信心去完成第三次作业。
同步块和锁的选择
第一次作业
第一次作业由于不了解同步块和锁的原理,因此我使用了WaitQueue的方式来尝试解题。
虽然未完成,但我认为代码中有几个代码是自己以后应该多留意的。
以下是Input模块里的run方法,这里外环使用了while(true),但我却忘了在输入结束后需要break掉,相信这也是导致CPU TLE的原因。因此以后使用这类while循环语句时要加倍注意。
第二次作业
第二次作业由于有了第一次作业的经验,解决了在输入结束后依然继续执行的bug。这一次作业使用了集中式调度,使用 synchronized同步块将waitQueue同步于所有电梯和输入间,让所有电梯在空闲时都可以自由抢占waitQueue里的乘客。
第三次作业
第三次作业由于出现了不同型号的电梯,导致第二次作业写的集中式调度出现了问题。因此决定改成全分布式,让所有电梯都拥有自己的等候队列,之后再使用调度算法将乘客分给电梯。
调度器设计
第一次作业为单电梯,因此没有特别设计调度器,而是使用了input模块将输入放入waitingQueue,再直接将waitingQueue传参给电梯模块。
第二次作业只是单纯的从单部电梯升级为多部电梯,因此代码可以说是第一次作业的微改版,只是使用了同步块将waitQueue同步于input模块和所有电梯间,让所有电梯自由抢乘客,同时避免不同电梯抢同一位乘客的情况。
第三次作业添加了不同型号的电梯,调度方式也从前两次作业的集中式改成了分布式。因此使用了一个简单的调度算法来调度乘客给各个电梯。
第三次作业架构设计的可扩展性
第三次作业的架构设计主要为输入,电梯,等候乘客队列类。
说实话这次作业的可扩展性并不好,因为代码是围绕着实现题目所要求的功能为主要目的来编写的,加上没有实现换乘,因此性能也不好。
这次的第三次作业由于个人因素导致时间紧迫,因此并没能在截至之前完成优化。电梯是收到一个乘客请求,就载一个人到目的地,这导致强侧全都RTLE了。
但已经想好优化方案,即让电梯等几秒,直到乘客累计到一定的量之后再开始载送。我写完这博客之后会继续努力尝试优化。
UML图
分析自己程序的bug
第一次作业
CPUTLE
这个bug到截至的时候依然找不到原因,之后发现原因就出在input模块的while语句在输入结束之后并没有break。这会导致输入结束之后,电梯退出了,input模块却还在等待输入。
第二次作业
乘客服务过快问题
指导书是说,开门或关门的0.2s期间乘客仍然可以进出,纸片人没有厚度。但我却忽略了开门和关门的时间顺序,代码写成了先过0.2s后才开门,再0.2s后关门。这个bug自己找的时候找不出问题,是问了朋友之后才知道的。
第三次作业
强侧RTLE
这个主要是因为电梯一开始使用了while循环语句和sleep的方式来等待输入,导致线程有可能一直霸占着waitQueue。这个问题在之后研究了wait和notify的用法之后就得以解决了。
心得体会
个人认为这一单元的作业难度整体比上一单元的作业好一些。
线程安全方面,经过了这三次的作业,深刻体会到了对一个对象进行同步加锁的重要性,像是第二次作业提交时就有出现过乘客还未到达,电梯就已经退出的情况。再来就是CPUTLE的原因,这让我对wait-notify方法有了更深入的理解,也了解到sleep并不是什么都可以解决的。
至于层次化方面,这三次的作业也能够感觉出来。在设计结构的时候就应该根据功能对对象进行分离,这可以确保代码有更好的扩展性。就像是第二次作业和第三次作业里,电梯其实基本上都是一样的,需要更改的只不过是调度器的调度策略罢了。有了层次化设计之后,重构的次数也确实少了。