OO第二单元作业总结

OO第二单元作业总结

设计策略

  总共有三类线程:主线程、输入线程、电梯线程。调度器Scheduler作为共享对象,输入线程向其中放入指令,电梯采用scan算法,到达每一层时可以根据自身的状态、楼层、属性等信息从调度器中主动取出一定数目的指令,放入自己的内部队列中。
  在多线程同步方面,主要是将scheduler的增删改查指令加锁。同时,当调度器内部没有指令的时候不做等待,直接返回一个空的Arraylist,使得访问的效率较高。不好的一点是通篇没有用到wait-notify,与本单元对多线程编程训练的目标不符。但是结构简单,从第一次到第三次作业几乎没有大的变动,成功体验了较为舒适的迭代开发经历。
  在第三次作业中,考虑到换乘的请求,依靠输入线程结束和调度器为空已经不能够作为电梯线程结束的标准,否则可能出现A类电梯早早结束,然而B类电梯又产生了一个新的A类电梯请求没法被送到的情况。最终找到了一个较为稳妥的方法:在调度器内部维持一个unfinished变量,输入线程每次往里面放指令就递增它。电梯每到一层,将特定人群放出时,会将人返还给调度器(feedback),由调度器检查当前楼层与请求的目标楼层是否相符,如果相符就将unfinished递减,否则将请求放到新的楼层容器中,以便后面的电梯继续执行它。电梯只有在输入线程结束、调度器的unfinished==0的情况下才会停止。这个方法比较好地解决了电梯线程结束的问题,但是由于笔者的笔误,在5楼返回指令时写的楼层号是3,导致几乎所有下到3楼的指令无法送达,第三次强测分数惨不忍睹,现在想起来依旧心痛。

第三次作业的可扩展性

  因为求稳(虽然最后还是翻车了),所以对性能一直是一种随缘的态度。特别是第二次,用随机打败随机:为了防止多部电梯一起往上走,我甚至在第二步以后的电梯刚开始的时候先睡一段时间,以便拉开差距(顺便有概率积累一部分可捎带指令,最终强测结果99+,还是很不错的)。但是这样的算法不智能,性能得不到保障。
  如果在不大改动第三次作业架构的情况下,如何再提升性能呢?因为基本架构是电梯去主动抢指令,而不是调度器主动将指令分发给调度器,因此能够扩展的地方只有在电梯“观望”调度器指令情况的时候进行优化。可行的优化策略是,当电梯内部指令为空的时候,调度器根据据当前楼层的距离和等待时间算出哪一头的乘客数目多、需求更紧急,然后决定电梯是掉头还是继续向前。同时将scan算法可以优化为look算法。

基于度量分析自己程序的结构

第一次作业:
OO第二单元作业总结_第1张图片
可以看到类图是比较简单的。至于代码复杂度与第二次作业及其相近,为了节省篇幅就放在下面了。
第二次作业:
OO第二单元作业总结_第2张图片
从类图上也能体现出代码结构的可复用性。
OO第二单元作业总结_第3张图片
OO第二单元作业总结_第4张图片
这里面飚红的是scheduler.peek()方法,它的ev有点高,即非结构化程度比较高。他是电梯到达每一楼层的时候向调度器内部“瞄一眼”的动作。
第三次作业:
OO第二单元作业总结_第5张图片
第三次作业为了输出线程的安全将课程组提供的输出线程封装了一下。同时,A、B、C三种电梯继承自电梯类,复用的代码部分也是比较客观的。
复杂度:
OO第二单元作业总结_第6张图片
比较高的仍然是几个基本复杂度。这说明我的代码在结构性方面还有待改进。

程序的BUG

  第一次作业与第二次作业虽然强测与互测没有测出来BUG,但是在写第二次作业的时候发现了第一次作业的一个BUG:那就是在判断电梯进程是否结束的时候,没有判断电梯内部是否还有人。这就导致可能存在电梯到了最高层或者最低层、输入线程结束、电梯从调度器取了指令是它为空后,就不动了......第二次作业更正了这一BUG。
  第三次作业的滑铁卢BUG上面已经提过了。可笑的是,我再本地还将所有的506条指令都跑了一遍,不过当时只是判断电梯线程能否正确关闭,没有考虑是否把人送到了目的地。可以说这是不写评测机的报应吧(虽然不写的主要原因是电脑的jre和jdk不是一个版本,要是重装配环境变量的话怕伤筋动骨,但这不应该成为我不充分测试的理由)。

发现别人BUG

  因为没有写评测机,所以找别人BUG主要是基于程序的,或者手动构造。不过数量并不是很多。

心得体会

  最大的心得体会就是要做足测试。不要盲目相信自己的程序没有BUG,测试是程序开发中不可或缺的一环。另外,有得必有失吧。为了结构的简单可靠,牺牲了性能以及更多的练习多线程的机会。希望以后还是能多练一练多线程中的其他模式和容器。
  OO之旅已经过半,经过两次OO作业强测滑铁卢之后,我对成绩看的似乎没那么重了。那就希望我之后能够更加谨慎、更加充满想象力,不要因为一个分数禁锢了自己的手脚。

你可能感兴趣的:(OO第二单元作业总结)