BUAA_OO 第二单元捎带式电梯调度作业总结
一、 作业总结
这三次作业是关于调度捎带式电梯的一个层递式project。这三次作业主要是关于多线程的一些研究。以下是针对三次作业不同设计思路的总结。
第一次作业:
实现方法:
第一次作业是对单部可稍带式电梯的调度问题。这个问题是我第一次涉及多线程的任务,在线程的处理之上可能存在一些不太合理的地方。我在了解多线程之后,对于这个问题我最先联想到的是生产者-消费者模式。如果说把一个个乘梯的请求看作一件件产品的话,那么可以将输入看成生产者不断的输入请求,将电梯看成消费者来处理这些请求,而用调度器作为一个托盘来装这些请求。基于这个思考,我设计了一个主类MainClass,一个调度器类Scheduler以及电梯线程Elevator和输入线程Input这两个线程。在Scheduler类中,我采取了ArrayList这个容器来存储请求。在其中使用wait方法和notifyAll方法来解决暴力轮询的问题以节省cpu时间。而Input线程则是利用课程提供的包来对输入进行处理,并不断将请求放入scheduler中。而对于Elevator线程,则是此次作业的重点。首先是关于电梯调度的策略,由于此次是可捎带式电梯,所以我采用的较容易实现的look算法来处理这个问题。基于这个算法,对一个电梯来说其行为可抽象为移动、上下乘客和变向。所以在Elevator类中我分别实现了trans、inout和reserve三个方法来完成对应的三个操作。到此为止,整个电梯架构就已经完成了。而在这次作业中需要注意的就是结束的判断。
代码度量:
第二次作业
实现方法:
第二次作业是关于第一次作业的一次拓展,这次作业在上次的基础上增加了电梯的数目,由一架电梯变成了一至五架电梯。另外还有一个不同之处就是增加了负楼层。首先我是先针对一架电梯做了对楼层数增加的处理,这个地方需要注意的是没有0层。在处理完一架电梯后,我在将其扩展为多架电梯。在这里,我的想法是使用5个电梯线程来分别处理5架电梯的任务。首先通过输入的电梯数目来决定star哪些电梯线程。然后是对于请求分配的处理。囿于对电梯调度的了解有限,我采用了平均分配这些请求,这样在性能上就有一定的损失。由于强测翻车了(在后续bug分析处会提及),也没能看到最终的性能情况,从第三次作业来看这样的写法确存在提升空间。
代码度量:
第三次作业
实现方法:
第三次作业在前两次的作业基础上有两个地方有较大的改动。一个是针对电梯,规定了不同的电梯类型,不同类型的电梯有着不同的最大载客数、可停靠楼层和运行速度。另外一个就是增加了新增电梯的操作,通过新增电梯可以在原有的三架电梯基础上新增不同类型的电梯。这次作业的难点我觉得一个是换乘的处理,另一个就是新增电梯的处理。对于新增电梯,我处理的很是粗略,我采用的是如果get到一个新增电梯的请求,就新增一个该类型的电梯线程。而同一类型的所有电梯共享同一个请求队列,每个电梯线程来自由竞争这些请求队列之中的请求。由于这样的处理,在性能分上还是有较大的损失的。而对于电梯换乘,我通过对不同电梯停靠不同楼层的分析,绘制了一个关于电梯换乘的表格来分析电梯换乘的情况。
通过表格可以发现A类电梯在1和15楼换乘,B类电梯在1、5和15楼换乘,而C类电梯也在1、5和15楼换乘。所以在电梯运行到换乘楼层时只需判断如果电梯内存在目的楼层该电梯不可达的情况就出电梯,并产生新的请求再加入到请求对列。在处理请求进入电梯的时候要特别注意从2楼到3楼的请求和从4楼到3楼的请求。这两个请求的换乘方向与目的方向是相反的,所以这个地方需要特殊考虑。
代码度量:
二、bug分析
1.强测
第一次作业强测中我没有出现bug。第二次作业的强测则出现了一个很致命的问题,那就是关于输入的时候电梯吃人的问题。这个问题是因为在main里面一个Sysytem.in而在电梯输入类里面也一个System.in导致的。而这样就有可能导致输入的前面的的信息会在读取电梯数目之后进入到了main函数里面的缓冲区域,而不是进入到了电梯输入的缓冲区,导致信息丢失了。由于这个问题我强测没有通过而导致未能进行互测,以后遇到此类问题一定要谨慎。而第三次强测中我则是出现了电梯逻辑的一些问题。在处理换乘时,我在判断电梯是否停下来的时候就将新的换乘信息加入到了请求队列,而此时这个请求可能还没有出电梯。这样就有可能会出现在换乘楼层一个人先进电梯再出电梯的逻辑问题。针对这个问题,我修改了换乘请求加入请求队列的时间从而修复了这个bug。
2.互测
由于这次电梯作业的种种情况,我此次互测的参与度较低,没有去hack别人。不过通过互测学习了一些同一个屋内同学的代码,还是有所收获。
三、创建模式分析
这一次多线程作业我采用的是生产者-消费者模型。这次作业中,我将调度器作为一个对象,也就是生产者-消费者模型中的托盘。其中来储存各个请求并完成请求的分配。而Input类是生产者来产生各个请求,Elevator类则是消费者来处理这些请求。这样的模式我从第一次作业一直沿用到了第三次作业,可见这个模式在此次多线程作业之中还是能够在一定程度上来解决一些问题的。
四、心得体会
在不断的深入java学习的过程之中,我对面向对象的思维有了更加深刻的理解。有了第一单元作业的经验,我在处理第二单元的作业的时候就更加注意了代码的可迭代性。这次的三次作业没有像之前一样每一次都需要来进行重构。总体来说在OO道路上还是在不断前进,但是还是有许多细节需要注意,同时在完成自己代码的同时也可以多看看别人的思路,或者是多逛逛讨论区,向他人学习。