其他分享
首页 > 其他分享> > OO_第二单元总结

OO_第二单元总结

作者:互联网

oo第二单元主要是Java多线程电梯问题,第一次作业是纵向电梯,第二次作业增加了横向电梯,并且可以动态增加电梯指令,第三次作业支持乘客换乘。性能分主要取决于不同的调度策略,即如何把所有乘客在最短的时间内送到目的地。

整体设计

一、所采用的策略

第一次作业策略,我将乘客依据不同的楼层,以及同一楼层的上行和下行请求设置了不同的队列,一共有20个队列,考虑到我们平常不会乘坐和目的方向运行相反的电梯,电梯中的乘客,要么同时上行,要么同时下行,在电梯没人时扫描本楼的所有队列,去接离电梯现在所在楼层最近的乘客,否则等待。强测大概有4个完全没有性能分(85)。

第二次作业,我参考了往届学长的博客,选择了LOOK策略,具体来讲是,电梯可以同时搭载不同方向的乘客。电梯到一个楼层,只要这个楼层有乘客,只要电梯人数没满,乘客就可以上电梯。电梯在运行时,如果本方向上还有乘客的目的地,或者本方向上的楼层还有请求,电梯就可以继续运行,否则,如果电梯乘客数为0,等待请求,如果乘客数不为0,就改变运行方向。

第二次作业同时采用了量子电梯。

量子电梯

假设电梯运行速度为0.4s,电梯在楼层a,那么电梯运行到下一层b所花时间为0.4s,其实可以等价于电梯在上一个状态(输出到达a层Arrive,或者输出在a层关门Close)后0.4s即可输出到达b层的信息。假设0.4s后b层有请求,那么我们不需让电梯等待0.4s,可以直接输出arrive b。

开关门期间可以上下人

电梯花0.2s开门,花0.2s关门,等价于在开门0.4s后输出关门信息。纸片人没有厚度,在0.4s时间内可以任意进入电梯,所以在电梯关门0.4s时间段,如果有乘客到达本层且电梯乘客未满,可以打断wait,让乘客进入。之后继续wait,不过wait的时间减少。

优先接目的地在电梯前进方向上的乘客

第二次作业做了这些优化后,发现性能提高仍不明显,所以又做了一个优化,把第一、二次作业结合,即乘客上电梯时,优先选择目的地是在电梯前进方向上的乘客。改了之后电梯性能大有提升。强测只有一个89,其他都是99、98的样子。

第三次作业采取了自由竞争策略,电梯在有乘客时都可以去接人,先到先得,增加了换乘策略。和同学讨论过如何保证两个电梯在同一层接人时,优先让更快的电梯先接到,没有什么结果。我考虑过设置线程优先级,但是优先级高的线程似乎只是分到的时间片多,和我的目的也不太一致。由于时间问题,我也没做相关的优化。

第三次作业,自由竞争策略很简单,结果也不错,课程组的性能标准给得又很温柔,强测没有低于99分的。

二、同步块与锁

同步块的设置和锁的选择

由于作业时间有限,我只采用了synchronized将对象作为锁,没有使用ReentrantLock和ReentrantReadWriteLock读写锁。对这两个类没有太多使用经验,担心出bug也是一个原因。

而且性能分主要还是由电梯移动时间和开关门时间决定,我也没再追求这方面的细节优化。

电梯在关门窗口期间使用了同步块,创建了一个新的共享变量lock,synchronized对象即为lock,lock拥有电梯的相关信息,lock也被等待队列共享,如果在开关门窗口期间有合适的乘客加入,等待类会通过lock唤醒电梯。

这样实现了关门的0.4s可以在有乘客到来时唤醒电梯,但可能还有更好的方法。

锁与同步块中处理语句之间的关系

三、调度器设计

大致思路是,输入是一个线程,当输入要求新建电梯时,直接用工厂模式新建电梯,如果输入的是乘客请求,就放入waitPeople队列。同时还维护了纵向电梯的请求类和横向电梯请求类,调度器从waitPeople里拿请求,如果判断是纵向请求,就放入纵向请求类,如果是横向请求,就放入横向请求类,如果是换乘乘客,就特判。

纵向电梯只从纵向请求类里面接人,横向电梯也只从横向请求类里接人。

四、架构模式

采用了流水线架构模式,主要针对换乘乘客,由于每座楼都有纵向电梯,但不是每层都有横向电梯,故会遍历所有现有的横向电梯信息,选出最佳中转楼层。由于纵向电梯的请求类ClassdedBuild和横向电梯请求类ClassedFloor是根据乘客的请求信息分类,故在电梯将请求放入两个类之前,要修改相关请求信息。
image

1.UML协作图

image

2.UML类图

image

Bug分析

一、自己的bug

强测均无bug

第一次作业被hack的点是我没有注意到官方输出包是线程不安全的,需要自己维护输出时间戳递增。

第二次作业没被hack,但是本地发现了一个逻辑错误。主要是else没特判,可能会到11层或0层。

第三次作业,在提交截止前两个小时,我才发现没有考虑到,横向请求也可能是换乘的请求(因为本层可以没有横向电梯)。我把横向请求最初都加到了横向请求类,如果接下来一直没有新加本层的横向电梯,这个人就出不来,程序也就停止不了,TLE

对于这类请求,最初应该加到ClassedBuild类,并且在特判是否是换乘乘客时增加这种情况。更改不到20行就可以修复bug。

但是最后两小时,提交间隔又是15min,比较急,我修改了好多代码,还是没赶得上。

本来以为强测铁定过不了,说不定全tle,互测都难进。哪曾料到强测所有的测试点都完美避开了我的bug。加上程序性能还可以,结果还挺好。

因为这个bug,互测被hack的测试点,80%都TLE。(没错我就是Saber)
难为大家了,都不敢放开手hack,o(╥﹏╥)o。

二、hack策略

随机生成数据黑盒hack,利用有限状态机检测正确性。

只在第一次作业hack到了一个没有用迭代器,边遍历,边删减容器的同学。

第二三次在本地自动评测机跑了好久,要么没发现别人的bug,要么就是发现了bug,交上去,由于多线程的不确定性没有hack成功,我也没再多交。

还有一个和同学交流发现的bug,有的同学用的notifyAll次数太多,一些不需要用的地方也加上了,加上本身程序设计的特点,造成了轮询。

我删除了不必要的notyfyAll,发现CPU利用率确实下降了好多。

感想与总结

线程安全

对于共享对象基本上所有方法全加了synchronized,简单但是性能不如读写锁,以后可以尝试一下使用读写锁。

遇见过一次死锁问题,就是同步代码块包含了一个可以不包含在这个代码块里的调用,二者形成了死锁,把同步代码块变小一点就可以了。

层次化设计

一共有三个等待类,一个是Input线程输入的等待类,一个是调度器分派出横向电梯等待类,纵向电梯等待类。每个电梯配置了一个乘客类和策略类,电梯负责改变电梯状态,乘客类负责乘客上下电梯,策略类综合各种信息给出电梯下一状态。

感觉还可以再优化。

感想

自己的评测机还是不够好。第三次作业我随机生成了大量换乘乘客(指出发座和目的座、出发层和目的层都不相同)以及加减电梯的信息,没有发现bug。

吃完饭后回来想想,我是不是再调调乘客各种种类的比例?然后在截止前两小时发现bug。image

回想起我第一单元也是sum忘记判断负数,也是强测过了互测被hack惨了,历史总是惊人的相似。但我不觉得下一次能有这么幸运了。

得好好吸取教训。思考全面一点,自己设计的评测机也要多多关注各种边界情况。

其实第一次听课之前我对多线程还不是很了解,只是简单知道一些基本的线程创建运行知识。之后到图书馆恶补了Java多线程,就像马原老师说,实践是认识发展的动力,果然是有了ddl才最能激励人去学习。

希望以后可以规划好时间,可以做到预习叭。

标签:OO,总结,请求,乘客,横向,电梯,线程,bug,单元
来源: https://www.cnblogs.com/eiang/p/16191649.html