OO第二单元小结

时间:2021-06-22 02:11:38

OO第二单元小结

一.三次作业代码分析。

1.第一次作业

第一次作业是单部电梯的傻瓜调度,由于其过分傻瓜,所以第一次作业我只有两个类,一个main,一个电梯,main类负责不断从输入流中读取命令,如果输入空则退出,否则将这条命令交给电梯去run,而电梯需要做的事情就是从当前楼层去接人,然后将之送达目的地即可。第一次作业基本没有使用多线程的思想,但是正确性是无懈可击的。

其实为了学习和使用多线程知识,我也写了一个多线程版本的傻瓜调度,其设计与第二次作业几乎相同,不同点就只在电梯的运行方法上有所不同。

类图:

OO第二单元小结

度量分析:

OO第二单元小结

OO第二单元小结

2.第二次作业

第二次作业相比第一次作业增加了电梯的捎带,电梯需要实时根据自己的状态来判断是否有可以捎带的命令在请求队列之中。所以第二次作业不能使用第一次作业的方法,于是我便开始了灾难性重构。

这次作业使用了5个类,main类负责构建输入流线程和调度器线程,两个线程共享一个请求队列;调度器创建电梯线程,并且和电梯共享一个待执行命令队列。由于只有一部电梯,所以调度器要做的事情就是当请求队列不空,则拿出一条命令放到电梯线程的待执行命令队列之中,使用sleep加轮询的方式防止cpu超时。电梯要做的事情就是,根据待执行队列中的命令和自身状态来进行接送乘客的操作。因为将电梯如何运行的操作全部放在了电梯线程之中,所以电梯类有些臃肿。

类图:

OO第二单元小结

度量分析:

OO第二单元小结

OO第二单元小结

3.第三次作业

第三次作业相比第二次作业,就是多了两部电梯和电梯有一定的停靠规则以及运行时间上的差异。但是由于第二次作业构建得比较成功,所以第三次作业我做的相对轻松(正确性),只需要在调度器线程中再开两部电梯,调度器根据一定的算法来分配命令,不能直接送达的命令则拆分成两个部分,第二部分放到新建的单例模式类的请求队列之中,用来管理第二部分指令,每当电梯到达某一楼层有人出来之时,就在该单列出来的队列之中遍历有无相同id的命令,有的话则将之从该队列之中删除,并且将之加入到输入流和调度器线程所共用的那个请求队列之中,等待调度器进行下一次分配即可。

类图:

OO第二单元小结

度量分析:

OO第二单元小结

OO第二单元小结

OO第二单元小结

二.BUG分析

第一次作业

(1)由于采用了简单暴力的解法,所以第一次作业没有bug。

第二次作业

(1)电梯状态的初始化没做好,导致会有人还没有进来就出去的错误。

第三次作业

(1) 第三次作业多了一个第二部分请求队列,所以一开始判断结束条件时少了判断第二部分请求队列是否为空,导致进程不能正常结束。

三.互测攻略

这三次作业,我每一次都是将每个人的src放在不同的包中,先大致浏览一下代码,然后一个一个进行手动测试,测试的数据是自己课下以及在互测阶段出过bug的数据、自己构想的一些边界数据以及电梯第一次作业中的强测数据,在电梯第二次作业中取得了比较好的hack效果。

四.反思与总结

总地来说,三次作业的难度区分度还是有的,在逐步完成作业的过程中,我其实遇到了很多线程安全上的问题,主要原因在于对wait和notify还理解得不够透彻,所以还是采用了sleep加轮询的方法来消除忙等待导致的cpu超时,以及使用JAVA自带的线程安全的CopyOnWriteArrayList和一些synchronized方法来避免线程不安全,是属于比较笨拙但是却有效的方法来解决安全问题。

其实,性能方面也是我要惭愧的一个地方,由于能力有限,每次作业我以正确性为最高目标,完全没有考虑架构的优化潜力,这最终导致我的强测分数并不好看。但是正如助教所说,不必为了性能而大开倒车。冒着风险去做不太符合现实场景的优化(预测未来什么的),不仅仅是因为要花费的时间很多,而且我也不太愿意去做和现实实际场景不太相符的优化。但是抛开现实场景,我觉得学习优化的方法还是对自己大有卑益的,所以我以后会尽量改变自己的态度。