OO-第二次总结
(1)同步块的设置和锁的选择,锁与同步块中处理语句直接的关系
- 设置Elevator(电梯对象)和Input(读入方法)两个线程,同步运行。
- 为了避免电梯在无需工作时轮询,将电梯的行为用synchronized封装起来,当满足“请求队列为空”、“电梯内无乘客”和“输入未停止”时,电梯用wait()停止,直到Input再次检测到输入数据,用notifyAll()唤醒电梯。
- 第二、三次作业中加入了多部电梯(多线程),为了避免电梯之间冲突,对每一个对Queue(请求队列)的读写操作用读写锁封装。
(2)调度器设计,调度器与程序中的线程交互
- 第一次作业:采用ALS策略,并没有设置单独的调度器模块,整合进了电梯行为中。
ALS策略即:设置一个请求为主请求,电梯执行主请求,并在运行途中捎带副请求,当主请求完成后,从副请求和请求队列中再次选择一个主请求。以此类推。 - 第二次作业:增加了多部电梯,单部电梯仍按ALS策略运行。
因个人能力有限,没有想出合适的统筹规划方案。我采用的策略是让多部电梯“竞争运行”,没有使用单独的调度模块。
所谓“竞争”就是例如当第一个请求进入队列时,不进行调度,所有电梯都设其为主请求,一起向该请求的起始楼层前进。由于使用了读写锁,最终请求只会进入一部电梯(此处具有一定随机性)。其余没有接到请求的电梯会重新设置主请求,以此类推。
对于随机数据而言,没接到主请求的电梯看似做了许多“无用功”,实际上性能并不会变差。但由于策略具有随机性,可能出现极端情况。 - 第三次作业:对电梯进行了分类,在第二次作业的基础上加入了一些贪心策略。
因为C类电梯效率显著大于B类,B类又显著大于A类,因此我的策略是将请求分类
1.“能被C类电梯接的长途移动”为C类
2.“非C类而在奇数楼层进出”为B类
3.其余为A类。
C类请求由C类电梯接,B类请求由B类电梯接,A类请求由A类电梯接。不进行换乘操作。
在Input模块里对请求进行分类,实际上是将Queue分成了A、B、C三部分,三类电梯分别对三个部分各自进行调度。同一类中的不同电梯仍采取第二次作业中的竞争制。
(3)架构设计与可扩展性
- 第三次作业
- 类复杂度
Class | OCavg | OCmax | WMC |
---|---|---|---|
Elevator | 4.44 | 7 | 40 |
Input | 3.5 | 9 | 14 |
Mainclass | 1 | 1 | 1 |
Queue | 1 | 1 | 5 |
- 方法复杂度
Method | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
Elevator.Elevator(Queue,Object,int,ReentrantReadWriteLock,int) | 3 | 1 | 1 | 4 |
Elevator.check(PersonRequest) | 5 | 5 | 5 | 6 |
Elevator.getFirstRequest(Queue,int) | 3 | 3 | 2 | 3 |
Elevator.judgeFlag(PersonRequest) | 2 | 2 | 1 | 2 |
Elevator.leave() | 14 | 1 | 6 | 7 |
Elevator.move(int) | 14 | 1 | 4 | 6 |
Elevator.pick() | 18 | 1 | 10 | 11 |
Elevator.run() | 13 | 1 | 9 | 9 |
Elevator.setMainRequest() | 4 | 1 | 3 | 3 |
Input.Input(Queue,Object,ReentrantReadWriteLock) | 0 | 1 | 1 | 1 |
Input.judgeType(PersonRequest) | 7 | 3 | 1 | 11 |
Input.run() | 17 | 1 | 9 | 10 |
Input.setLock(Object) | 0 | 1 | 1 | 1 |
Mainclass.main(String[]) | 0 | 1 | 1 | 1 |
Queue.getQueue() | 0 | 1 | 1 | 1 |
Queue.getStop() | 0 | 1 | 1 | 1 |
Queue.getfirst(int) | 0 | 1 | 1 | 1 |
Queue.setStop(boolean) | 0 | 1 | 1 | 1 |
Queue.setfirst(int,int) | 0 | 1 | 1 | 1 |
Elevator.pick()复杂度最高,负责处理开关门、请求进入电梯、请求队列的重新排列。 |
- UML类图
- UML时序图
- 可拓展性
- 没有设置单独的调度器,策略被整合在Input中,可拓展度较低。
(4)自己程序的bug
- 第一次作业在强测和互测中没有被hack
- 第二次作业在互测中没有被hack,在强测中RTLE了一个点,但在课下自己测试时却能通过,疑似是策略随机性的问题或是服务器与本地行为不同。
- 第三次作业强测通过,互测中被hack了一个点。是全为A类请求的极端情况,我的策略无法通过。
(5)别人程序的bug
- 本次作业并没有hack成功orz
- 三次作业中分到的小组成员被hack的点均为RTLE,是策略不当的问题。
(6)心得体会
- 反思
- 本单元偷懒采用了电梯的竞争制,没有思考出合适的调度策略,导致极端情况过不了。
- 对于Morning和Night情况没有做单独分析,全部使用了Random模式的策略。
- 在写第二次作业时自己造了好久轮子,没有用现成的读写锁,浪费了大量时间。
- 心得
- 本单元比上次进步的地方在于,第一次作业加了把劲,实现了推荐的ALS策略,使得第二和第三次作业的迭代变得轻松很多。
- 学习了java多线程和锁的使用,进一步理解了面向对象的思想。
- 总结
- 本单元比上单元轻松了许多,希望下个单元也能再接再厉。