第二次OO作业总结

这一次作业开始引入了线程的概念,以电梯的形式进行考察

先大概说一下这几次的设计思路吧

从最开始的傻瓜,到捎带,再到多部可捎带(限制经过楼层),其实说实话我一开始并不懂多线程的写法,第一次作业完全可以说就是单线程的傻瓜式解决问题;第二次其实也是个单线程,只不过是每一层都会考虑捎带条件,使得一趟能尽可能地多带一些人而已。

如下为第一次和第二次的大致架构:

第一次就全靠这一个函数,粗暴地算,比我想象中的简单很多

第二次真正地采用了与线程相关的功能,而且参照了第一次互测某同学的系统化架构,将输入流,电梯,需求队列以及主函数全部分开

队列类当中存放着对应的PersonRequest的动态队列,以及对这个的存取以及各种判断等行为

输入流负责一直接收新的任务要求,电梯中负责计算楼层差,判断方向,以及单线程的动作

最后main函数再调取这些类,开启输入流和单电梯的线程

但是第三次要设计,很多的事情都需要重构,电梯的具体情况需要考虑的很多...

具体来说和第二次的整体框架的改革,就是加入了调度器,去掉了需求队列的类

(具体原因之后再解释)

调度器内部:

1.设有3个电梯对应的需求队列

之前的专门的类会存在线程安全的问题(如判断队列为空时,要去除或添加元素时需要wait和notifyAll),导致最终出现死锁,只能关闭输入流,程序无法结束,而且ArrayList也有对应的方法,所以就直接放弃多余的封装,直接声明动态队列即可

2.三个电梯的具体参数 (人员上限,经停情况)

这个自不必多说,就是初始化一下就好

3.定位3个队列或者总需求队列的函数

这个也不想多说了

4.判断对应队列是否超载,空,是否需要停止调度等函数队列,更新当前队列主次元素的函数

超载大概还得说说,不是说不加了,而是延迟,也放在这个队列,所以电梯也要对应一个队列,来取这里的

5.最重要的:分配任务allocRequest

从最早入队列的开始分配,穷举分类讨论,争取上运动最快的电梯,一趟不行就拆成多趟

电梯类

将开门,关门,人员出入,从电梯需求队列(调度器接口处)获得需求,更新队列主次元素,以这些底层函数来组成run

输入类没有变化

main函数对一些必要的量以及调度器做初始化之后,与输入流和3个电梯连接,多进程即可就此开始

整体的设计策略确实只是优先保证线程的安全性与结果的绝对正确性,所以几乎没有做什么优化,分配任务的方式(第三次)也非常简单粗暴,尽可能地上时间最短的电梯而已

接下来分析OO度量

第一次作业

第二次作业

第三次作业

SOLID罗列:

SRP:这个直到第三次作业,为了能够更好地完成调度方法,才彻底地将全部的职责分开。即便是如此,分配任务的调度函数由于用的是穷举的方法,还是超过了60行...

OCP:基本上只有输入类没有再修改,3次作业当中,电梯每一次都做了修改,新添了调度器,将所有的功能逐渐地建立明确的分工,之前实现的需求队列类最终实践证明完全是多余的...就全删掉了

LSP :电梯之间的关系是平行的,本身之前在第二次作业的时候想让电梯来继承需求队列的功能,然而最终放弃了。所以基本上没有用上父子类的继承

ISP:以最后一次作业为例,调度器就是抽象类,其中的输入流即接入标准输入的接口,并且与调度器相接,3个电梯接在调度器的分配任务端口,3个电梯的职能会接入标准输出,输出相应内容

DIP:官方给的jar包对输入输出的处理几乎完美,全部的异常情况都会输出。在线程当中,一旦涉及到sleep和close等问题时,就有可能输出异常,所以也依赖于抛出异常的输出流

BUG分析

目前没有在公测和互测中被爆出bug,那就只以我debug过程为例子吧

最大的问题就是第三次,对synchronize和notifyAll的用法不熟练,一旦过度的使用notifyAll的话,最终将会造成死锁无法结束,或造成线程的不稳定(如B电梯接受了两次同一个任务)

然后就是RTLE,是具体调度的一些问题,在第三次当中,一开始没有考虑到两趟以上的时候,就会出现要求无法全部调度完毕的情况。然后为了线程安全性,拆开的后一个任务必须接在最后面,对时间也就是很大的牺牲了

最后是CPUTLE的问题。这个事情经过查阅资料以及JProfiler的分析,最后发现主要是因为轮询的时候没有进行中途的暂停休息,导致中间长时间无限制的空循环,以致于整个程序在这段时间处于类似无限循环的状态,对CPU的影响极其高。

他人BUG分析

说实话这个我并不好说...一是我手上没有自己写出来的评测姬,没有黑科技;二是我一直龟缩B组,这几次作业也是基本以找出0个或1个bug作为结局;三是java的评测环境和评测机的评测环境不完全一样,所以两者测试结果也不完全一致;再有就是RTLE和CPUTLE确实不会在本地测试中被查出来。而且最后一个hack完全是靠盲炸蒙出来的...我到现在都不清楚具体他错在了哪里。

总之思路无非几种:

1.将一些指令输入的时间放在后面,空耗CPU占有率

2.强行做一些刁难的调度,累死电梯(一旦哪里稍有调度问题,就容易牺牲大量的时间

3.将一些看似同时间的指令调成0.几秒的前后,炸线程的安全性

4.超载问题和经停问题(第三次作业)

这几种情况其实除了4以外,全都可以靠中测的开放测试点解决绝大部分的情况。所以说互测查不出来也算正常...

与第一次OO作业的差异其实很明显...因为这里已经不需要我们对标准输入的信息做进一步的处理了(官方的jar包做得很好了),与爆WF这个思路可以说区别很明显。

最后的一些感想

 

线程的知识真的还是很有意思很重要的!一方面像计组的多周期流水,既不能一味地怂,全靠气泡混过去,也不能完全不顾,对于线程的安全和冲突也要有所考虑。另外,在隔壁OS的lab3处,也在认真地研究进程的中断与共享,队列调度等知识。将这些结合起来一块看,真的是有一种很相通的感觉!

另外,官方给的jar包确实对于我这种java小白拥有很好的借鉴和学习意义。对于各种变量的封装等知识都值得我仔细地分析一番。而且课程组替我们铺好了路,使得我们不再为类似“WRONG FORMAT”这种事情而头疼,再设置正则爆格式啥的,而是真正地让我们全心地投入到对调度队列的思考当中。

虽然我还是很怂,只追求全对,但是即使是这个过程对我来说,也并不都能算容易。在事后仔细分析其他人的思路,都能学到很多(如好的调度方式,LOOK和SCAN等等)

希望接下来的作业再接再厉!也衷心地希望这门课能够越来越好!!

猜你喜欢

转载自www.cnblogs.com/GoatGirl98/p/10754194.html
今日推荐