【面向对象第二单元作业】弄拙成巧的电梯优化——低并发不一定是坏事
【面向对象第二单元作业】弄拙成巧的电梯优化——低并发不一定是坏事
一、序言
经过三周的奋斗,传说中十分折磨人的电梯的三次作业也终于结束了。不过可能由于对java
编程的逐渐熟悉。这个单元的作业的成绩还略高于上一个单元。本单元的作业个人感觉有以下两个难点,线程安全与电梯策略。
二、调度器?可有可无(类调度算法的另一种实现)& 锁
1.调度算法 & 调度算法中的锁
在第一次作业开始时,进行了一次上机。上机的代码采取了调度器的模式来分配任务,进而完成对于不同任务分配给不同消费者这一行为,让大家初体验了一把调度器的作用。但是,前两次作业里本质上并没有不同的任务。这种情况下我选取了电梯竞争的集中式调度,上梯策略为同向出发地就近贪心、反向出发地向远贪心 (类似于LOOK算法
)。当电梯将目标纳入计划捎带队列中后则在总侯乘队列中删除它。在这样的单电梯策略下,若是采取高并发竞争,则会导致电梯较长时间的装载率不高。
下面举个例子
伪输入
1-from-1-to-20
2-from-2-to-18
3-from-3-to-17
对于高并发的情况,对于我的集中调度策略,则可能会将 3个需求分别纳入三台电梯的等待队列,造成不必要的浪费。
流程可能为
1号电梯拿到总需求锁 -> 用同向就近的需求填充等待队列(即仅在每单次填充时取得锁):即1号需求 -> 释放锁
2号电梯拿到总需求锁 -> 用同向就近的需求填充等待队列:即2号需求 -> 释放锁
3号电梯拿到总需求锁 -> 用同向就近的需求填充等待队列:即3号需求 -> 释放锁
于是我直接在电梯去刷新等待队列时将整个等待队列上锁,这样每个电梯都会得到一个连续的需求计划表。尽量避免了电梯赛跑的情况发生.
流程为
1-3号中一台电梯拿到总需求锁 -> 用同向就近的需求填满等待队列:即1,2,3号需求 -> 释放锁
再说回调度器,调度器的目标就是为了两个目的
- 同类任务分给同一消费者
- 消费者任务压力和消费者效率的比例相当
而在第二次作业中,我的电梯通过低并发的竞争,实现了类似的效果。
在第三次作业中,由于偷懒,不愿意去修改自己熬过两次强测的单电梯内容,选择了添加调度器来将不同类型的需求分发给不同的。优先分给效率最高的C类电梯然后是B类,接着是A类,并且根据比例做适当调整。由于并没有采用换乘策略,所以对于随机数据能有还算不错的性能,但对于针对性的数据,会出现A类有难,BC围观的场面。
2.线程交互
线程交互还是采取了生产者消费者模型进行的,通过共享容器来进行数据的交互。同时,为了防止电梯线程对于总队列的竞争过于激烈,输入或调度被阻塞。提升了调度器和输入器的优先级,降低被阻塞的概率。
3.锁的选择
由于采取了修改总等待队列的策略,为了保证线程安全,给等待队列上锁是不得不做的无奈之举。而且前文提到这个锁还带来一些附加的收益。这里就不再重复了。而对于自己构建的线程安全类,我采取了java内置的读写锁,来尽可能提升并发性。
4.优化方向
对于单电梯设计,在竞争策略可以修改改为区域贪心,即目的地尽量相近,让电梯在某一区域内运行。对分段式多电梯可以添加换乘策略,以应对特殊数据。
三、功能设计&性能设计
1.从UML类图看架构设计与可扩展性
总体逻辑为Main
构建共厂ElevatorFactory
然后通过工厂类的方法来构建并运行总电梯运行调度器(Scheduler
)、单电梯运行管理线程(BasicElevatorCommandCenter
);生成并将电梯属性信息存储类(Elevator
)传给相应的单电梯运行管理线程,生成并分配电梯结束信号(EndSign
)。然后由单电梯运行调度器(BasicElevatorCommandCenter
)来创建内部具体运行逻辑(ElevatorPattern
)并和该具体运行逻辑共享电梯状态与属性。同时 Main
构建并运行InputList
,构造并传入总调度器输入结束信号的EndSign
。
由于采取了工厂模式所以架构清晰且较强的拓展性。
2.从UML协作图分析功能设计与性能设计
由Main
线程创建并运行三类工作线程,并且由Main
来承担新构建电梯的功能。通过共享内存来实现数据共享,总体耦合性较低。结构较为简单。所以功能性较好,而测试也应征了这一点。而由于单个电梯对于Waiting List
上锁时间较长并发性较差,整体呈现出低并发。而对于电梯调度性能几乎没有影响。由于低并发,程序的稳定性较高,鲁棒性较好。
四、自身程序BUG
1.线程BUG
本单元作业中,由于前文提到的我程序的低并发性,只有第一次作业出现了线程唤醒的BUG,导致了中测有一个测试点未能正常停止导致了RTLE。除此之外没有其余相关BUG。
2.正确性BUG
在第二次作业中,由于更新队列时删除出错,导致了乘客丢失的问题。除此之外,在初次搭建过程中,经产会出现电梯来回横跳的现象。通常可以通过黑箱测试找到最小BUG触发条件,配合白盒分析来找到错误代码。
3.互测BUG
尽管在课下写了随机数据生成器,但是由于没有写正确性判断的自动化工具,最后也没有找到别人程序的BUG。
五、感想与心得
1.关于线程设计
在初学的时候,总是希望在线程设计上渴望高并发,但是高并发并不一定能得到最优解。尤其是在竞争策略中,若是为了降低单电梯之间的耦合,则需减少电梯间的通信。而高并发则可能每个电梯都不能的到局部最优解,低并发达到了类似于调度器的线性需求分配功能。
2.关于层次化设计
个人感觉本次作业的层次化设计较好,电梯间几乎没有通信,由Input
、scheduler
、singleElevator
、Main
四个模块分别承担了输入、调度、运行、增加电梯的功能。与此同时,通过工厂类来生成并分发scheduler
、singleElevator
中所需的共享容器实现层次化设计,增加了内聚性。