301
社区成员
发帖
与我相关
我的任务
分享
目录
第二单元主任务:模拟多线程实时电梯系统。
hw1:楼座内有6部电梯,接收乘客请求,并合理调度将其送到目的地。请求中包括乘客期待进入的电梯,电梯需要实时反馈运行情况(移动、开关门、乘客进出)。
hw2:新增电梯重置请求,电梯重置时可以改变最大容量和移动时间;乘客请求中不再添加电梯id,由调度器分配;电梯移动接乘客前需发receive指令,否则视为非法移动。
hw3:新增电梯重置为双轿厢请求,双轿厢电梯可以设置换成层。
三次作业中,我创建多线程的方法均为:声明一个Thread类的子类,在子类中重写Thread类的run方法。
均使用了synchronized关键字修饰方法,未使用其他类型的锁。
分析题目后,我设计的电梯系统运行逻辑如下:每部电梯有一个等候队列和轿厢内队列。等候队列存储”期望上这部电梯,但还没乘上“的乘客请求,轿厢内队列存储”已在轿厢内“的乘客需求。初始时刻,电梯停在一层,默认移动方向向上,若等候队列暂时没有乘客请求,且电梯内也没有乘客,该线程会wait,直到有新请求时被唤醒。若电梯在移动中,在每一层会检测乘客状态,检测是否有乘客需要进出电梯,若有,则当前层开门,若无,则继续前往下一层。电梯若有开门动作,先检测电梯内是否有乘客离开,若电梯还有空位,会依次让本层的等待乘客上电梯。电梯在到达最底层或最高层后,改变运行方向;或电梯沿当前运行方向无法到达等候乘客所在层,且电梯内无乘客,也会改变运行方向。
在讨论中,有同学提到了如下场景:电梯在某层接到乘客,在乘客关门后,电梯开始前往下一楼层时,突然该层到达了大量请求,电梯是否需要折返接人(以争取更短的总运行时间)。根据实际生活经验,电梯应该继续沿原路线运行,不能为了优化(一些情况下有可能用时更短)而放弃更符合认知逻辑的调度策略是不正确的。
电梯的等待队列,即Request中用到了锁,调度器向电梯分配请求,以及电梯查看请求过程中涉及线程交互。
UML类图如下。

类复杂度如下。Elevator类的基本复杂度较高,大概是因为我将电梯的运行控制(方向、开关门等)写在了一起。

在第一次作业的基础上,新增电梯重置请求,电梯重置时可以改变最大容量和移动时间;乘客请求中不再添加电梯id,由调度器分配;电梯移动接乘客前需发receive指令,否则视为非法移动。程序的基本架构未改变。
需要注意,本次乘客不指定乘坐某部电梯。经过思考与尝试,我认为新的调度器有两种解决方法:1.输入请求,查看6部电梯哪一部能最快接到该乘客(通过乘客所在楼层,6部电梯是否满载、楼层与运行方向确定),优点是在乘客依次到来时,能够尽量减少每位乘客的等待时间,但需要合理化调度策略,避免出错(如乘客数较多时,一些乘客可能无法被接上)。2.随机策略,通过随机生成电梯id的方法为乘客选择搭乘电梯,优点为,当大批乘客迅速到来时,随机分配时可能会拥有更好的性能,缺点为乘客请求数较少时,随即策略可能会导致一台电梯频繁工作。
电梯重置时,要锁住等待队列,使其无法被加入新请求。
类复杂度如下,同样的,Elevator类的基本复杂度较高。

双轿厢调度问题。在本次作业中,我采取了一个电梯分裂为两个电梯的策略,而非在程序开始时设置12个电梯线程,按需获取。本方法的优点是方便debug,缺点是牺牲了性能,策略非最优。

注意边界条件判定,电梯一开始会前往0或12层。
在大量乘客在同一时间到来时,可能出现乘客进入电梯后才receive的情况(不太容易复现)。
A、B轿厢在换乘楼层相撞。解决方法为,双轿厢电梯都不会在换乘层停留,在换层接到或放下乘客后会立刻离开。
电梯被重置为双轿厢后,未开始重置,却发出receive-A/B的指令。检查后发现,此时电梯等待队列未锁住,还可以添加请求。
线程安全:共享。本次作业中,涉及线程安全的部分为请求。调度器需要拿取请求分配给合适的线程,调度器的某些行为也要依靠线程反馈,关键是处理好他们的共享变量。保证线程安全的方法是使用synchronized(自动锁,锁的创建和释放都是自动的)、或者使用锁(lock,手动指定锁的创建和释放),或者用volatile关键字。本单元中只使用了第一种,在使用过程中要注意范围,避免不必要的性能消耗。
层次化设计:Elevator类仅处理电梯运行逻辑,不要夹杂调度模块。应该分为接收请求、分配请求、执行请求三个部分,使代码有较高的可读性和逻辑性。可采用生产者-消费者模式。
其他:第二单元结束,我和我愚蠢的电梯顺利通过三次强测。虽说三次强测都没有bug,勉强通过,但我明白,这是架构与程序可读性换来的,写最后一次作业时,我在迭代时能明显感觉到代码可读性变差。我还需要提升代码能力,在完成任务的基础上写出更好的代码。第三单元,预启动。