301
社区成员
发帖
与我相关
我的任务
分享oo第二单元是模拟多线程实时电梯系统,并熟悉多线程的设计方法,熟悉线程、锁、共享对象之间的关系。


本单元的电梯架构主要由Controller(控制器)、RequestQueue(请求队列)和Elevator(电梯)组成。InputThread将输入的请求Request加入到AllRequestQueue请求队列中,Controller从请求队列中获取请求并将请求分配给合适的电梯加入到电梯的请求队列中。电梯再从请求队列中取出请求作为电梯的mainRequest主请求,从而开始运行电梯。
在第五和第六次作业中,线程有输入线程,控制器线程,电梯线程(共6个)。第七次作业添加了一个EleController类对同一个id的电梯进行管理。若遇到重置电梯为双轿厢电梯的请求则增加多一个电梯线程。本单元采用的模式是生产者和消费者模式,其中输入线程充当了生产者而控制器线程和电梯线程则是同时作为生产者和消费者。AllRequestQueue和RequestQueue则充当了生产者和消费者模式中的托盘角色。通过设计同步块和锁来保证电梯正常运行。
第五次作业中,同步块只有RequestQueue,因为它作为共享对象,实例对象有两个,一个是总请求队列waitQueue,还有每个电梯的请求队列requestQueue。同步块用sychronize加锁,保证多个线程访问共享对象不会发生冲突。对每个共享对象的方法添加sychronized,保证共享对象每次只会被一个线程访问以避免数据竞争。当有线程访问共享对象,若共享对象被别的线程访问,线程就会被挂起等待锁的释放。若线程不满足条件则用wait()方法将线程挂起,等待其他方法用notifyAll()唤醒。
本单元三次作业中的调度器均为Controller类。Controller将waitQueue队列中等待的人按照一定的策略分配到每个电梯的请求队列中。由于第五次作业乘客指定了电梯,所以只需要将请求分配到相应的电梯就可以了。第六和第七次作业则需要自行设计调度器将请求分配给合适的电梯。在第六次和第七次作业中采用的策略时平均分配策略,将所有人平均的分配到每个电梯中。
电梯的运行策略采用的是look算法:
若电梯中主请求要到达的楼层在当前电梯的方向一直则运行方向保持不变
若请求队列中有乘客的出发楼层在电梯运行的方向前面,则保持不变
若请求队列中没有乘客的出发楼层在电梯运行的方向前面或电梯满人,则转换反向
若电梯无人且请求队列中没有请求则电梯原地等待
本次作业需要构建多部电梯,将乘客请求分配给电梯,在规定的时间内将所有的乘客送至目的地。
第五次作业相对比较简单,架构沿用了实验的架构并且顺利的完成作业。对于下一次作业的扩展,本作业新增了Controller类用于实现下一次作业的电梯分配策略。
在本次作业中没被hack成功也没发现别人的bug。
新增内容:
本次作业的架构与上次作业相差不大,做了一些调整,新增了EleThread类,电梯不再作为线程。
在接受到reset请求,电梯将在两层内进行重置。当电梯内有乘客,电梯将会在下一层将乘客全部送出。然后未完成的请求将作为新请求加入到AllreuqestQueue请求再为请求重新分配电梯。
在互测中被hack了一个bug,就是当5部电梯同时RESET时,所有请求将会分配给唯一一部没RESET的电梯导致RTLE。解决的办法是当有多部电梯同时在RESET时将暂停分配请求直到电梯完成RESET。
新增内容:
本次作业新增了一个ElevatorController类,用于管理相同id的电梯。当电梯遇到重置为双轿厢电梯请求时,会新增一个电梯和电梯线程并加入到ElevatorController类中以便于进行管理。
电梯换层与reset送出乘客相识,若乘客需要换层,当电梯抵达换层楼层将会将乘客全部送出。然后未完成的请求将作为新请求加入到AllreuqestQueue请求再为请求重新分配电梯。
当电梯将要前往换层楼层时,将会调用ElevatorController中的goTransferFloor()方法,表示电梯想要前往换层楼层。若换层楼层没有电梯,则该电梯获得锁直到电梯调用了leaveTransferFloor()方法表示电梯离开了换层楼层来释放锁。若换层楼层有电梯则进行wait(),直到另外一部电梯调用了leaveTransferFloor()方法来唤醒等待的电梯。
在互测中被hack了一个bug,由于缺少判断条件,导致电梯在接受到重置请求后多移动了一层导致reset没来两层内完成。
本次作业需要注意的问题是线程安全的问题。在本单元的作业中遇到了线程进入wait状态而没被唤醒导致rtle的问题。因此,需要适时的notify唤醒线程以避免所有线程都陷入等待状态。由于线程的bug难以复现,导致很难验证线程的安全。