此次做业完成了一个开环可选层电梯调度系统。第二次迭代加入了容量限制、多部电梯,第三次迭代加入了电梯楼层分工、增添电梯请求。java
同步控制的主要由Schedule设定,由Executor执行。并发控制的核心为一个阻塞定时监听器,可实现可调整的定时控制。这个模块的实现方法参考了java.utils.Timer
。算法
private long scheduledTime = Long.MAX_VALUE; public synchronized void retrieveNextAction() throws InterruptedException { long curTime = System.currentTimeMillis(); while (curTime >= scheduledTime) { wait(scheduledTime - curTime); curTime = System.currentTimeMillis(); } scheduledTime = Long.MAX_VALUE; return; }
scheduledTime
变量存储下一次计划动做的时间,规定该时间只减不增。即只保障不存在早于scheduledTime
的动做。缓存
retrieveNextAction
为阻塞方法,用于Executor阻塞获取下一次的动做。安全
存在新的预期执行的动做时,更新scheduledTime
,调用notifyAll(),retrieveNextAction
重置等待时间/取消等待。多线程
没有设定动做时序队列,每次完成时,须要检索所有可能的动做,并根据执行状况设置下一个scheduledTime。架构
这样作的主要优点在于没有用到Thread.sleep
方法,能够实现任意时刻对请求的及时响应。并发
另外,除Schedule外,另外一个共享变量为Elevator。这里Elevator类仅用做记录电梯状态,提供改变电梯状态的接口。全部操做由Executor根据Schedule产生,并在敏感操做(如关门-移动序列)的执行过程当中进行了上锁,屏蔽Schedule类的全部请求,保证一致性。机器学习
对于电梯移动,在Executor中对Schedule上锁保证安全性。对于人员流动,使用ConcurrentLinkedList定义可执行队列保障安全性,并在电梯移动时清空。Executor请求完成后调用相应方法通知Schedule,根据策略更新预期动做。函数
* 协做图中动做可能不知足正确性,但反应了动做之间的时序关系。性能
* 2个线程为MainClass主线程和Executor执行线程。Schedule、Elevator为共享变量,被动进行数据调取。
* 还存在Schedule-Elevator之间的调用。
本节以第三次做业的版本为准讨论了架构设计和可扩展性。
须要实现2个接口:IElevator、OperationMethod。
第三次做业经过继承的方法已经实现了楼层限制电梯,以及针对3中楼层限制电梯的方法不一样方法。
因为采用了策略与调度分离的架构,使得在实现相应功能的同时,为策略的制订保留了很大的空间,同时容许对不一样的策略进行试验。但在策略制订的过程当中,可扩展性作的不是很好,功能的调整每每须要策略作出较大的调整,在前几回做业中均进行了策略重构,可能也与使用的策略具备较高的特殊性有关。
单一责任准则:Schedule类和Executor类是全部请求实现的关键路径,对进一步添加请求不理想,应考虑创建请求接口,将请求执行步骤抽象为【电梯操做-调度更新-时间等待-新的请求】。
接口隔离:应考虑将策略接口拆分红发送策略接口、接受数据改变接口。
对于后续的扩展,能够根据扩展的类型进行版本迭代。
级别1:具备不一样楼层限制规则的电梯、更新评价方法。
扩展ElevatorRestricted.Type枚举类型,编写新的MethodTypeD,调整MethodTask3分配策略。
级别2:新的限制规则电梯/楼梯、不定时电梯。
创建新的Elevator类,重写Elevator相关方法,较大规模的调整MethodTask3分配策略,编写新的MethodTypeD。
级别3:新的请求类型。如:停用电梯、更改请求
在Schedule类中增添新的方法适配请求,检查Executor类的执行过程是否符合新条件下的请求要求。考虑IElevator、OperationMethod接口中是否须要增添方法适配请求。
若是请求的类型较多,能够考虑创建请求接口,将请求执行步骤抽象为【电梯操做-调度更新-时间等待-新的请求】过程,在Executor和Schedule中设立相应设施。
级别4:新的调度维度。如:水平电梯
创建新的调度策略。重构电梯类,将一维操做改成2维,并在全部访问位置进行修改。
第一次做业 | 第二次做业 | 第三次做业 |
---|---|---|
![]() |
![]() |
![]() |
第一次做业中对2种策略进行了测试,最终选用了略微优化的Als策略。另外一种策略过于复杂并且效果不是很好。第二次做业中对ALS策略进行了调整,以适配多电梯,效果相似于LOOK策略,但实现后致使MethodAlsMultiple复杂度太高。第三次做业将策略分红主策略和子策略,类复杂度能够接受,MethodAlsMultiple基本未做修改。
第一次做业 | 第二次做业 | 第三次做业 |
---|---|---|
![]() |
![]() |
![]() |
这几回做业设计中Executor线程须要完成所有动做的发送,工做量较大,一开始放在了run方法,尽管后来作出了一些步骤的提取,仍旧有不少步骤被留在了run方法中,致使复杂度较高,这个是不恰当的。
另外,在第三次做业中,部分优化方法直接进行了重复逻辑的复制粘贴,在一个方法内引用了较多外部参数,还须要进一步重构。
第一次做业 | 第二次做业 | 第三次做业 |
---|---|---|
![]() |
![]() |
![]() |
第一次做业因为分包不太恰当,致使根目录和子包出现了循环依赖,同时策略类和调度器也存在循环应用。第二次做业调整了Timer类的分包,创建了策略类和调度器的共有引用类,解决了这个问题。第三次做业因为子类实现了主类的内部接口,而主类有又持有子类的引用致使循环引用(因此根据规范应该把内部接口放在外部,或者编写额外的工厂类组装主类、子类)。
第一次做业中,中测阶段的bug主要为题目理解不当,没有正确的使用输出包,强测互测没有发现Bug。
第二次做业中,强测互测阶段也没有发现Bug。
第三次做业中,强测阶段没有发现bug,但互测阶段发现了两个较为严重的bug。一个是电梯容量定义错误,然而在以前的中测强测中因为数据较为均匀、稀疏,加上C电梯利用率较少没有被测出来,自测时也没有再作检查。另外一个是上电梯后,尽管已经通知了调度器更新,但在调度器更新函数的编写时没有考虑换乘问题,致使换乘时可能出现一我的上两个电梯的状况,在更新函数中加入一个删除相同请求就能够了。这个可能缘由是前两次做业调度器编写时内联逻辑过多致使鲁棒性不高,环境改变后忘记了相关内联逻辑。
使用本身编写的ScannerX实现了一个简易的定时输入,知足基本的测试需求。在test模块中,将main函数中的ElevatorInput替换为ScannerX便可测试。定时输入使用sleep进行定时,在第一次调用时初始化,根据当前时间进行修正。
实际上第二第三次互测中均没有发现其余人的bug,多是强测成绩比较好分到的屋大佬较多...第一次互测中因为定时输入没有作时间修正致使hack数据时间不许确,没有hack上。
和第一单元测试相比,此次测试评测机搭建更为麻烦一些,不像第一单元直接使用sympy计算就能够验证正确性,同时样例投放也更加困难一些。这致使测试的效率降低了一些。
此次是第一次完整地编写多线程协同程序。在并发程序的编写中,须要仔细地考虑冲突问题,但若是对于每个语句都进行考虑显然是不划算的。一方面是经典模型引入,例如读者、写者问题,生产者、消费者问题的解决方法,一方面是系统架构的独立性。若是将一个大的并发问题拆解成几个独立的链条,仅需考虑链条与链条的并发、链条内部的互访问,即可以“分而治之”,运用相关模型逐个击破。
在第一次做业中一开始尝试使用动态规划的思想进行优化,然而实际上效果并非很理想,并且复杂度很高,后来采用了改进的ALS策略。实际上和缓存机制相似,因为咱们并不知道将来可能存在什么请求,所以没法将其转化成背包问题,在请求来临前就实现最优调度。ALS和LOOK等方法看似和动态规划相比不是最优,但和SJF、FIFO等算法相似,都是一种基于已知内容的可行优化策略。
在第二次第三次优化的过程当中,基础上采用了ALS算法的定向捎带原则,并借鉴粒子群优化算法的思想,对空闲电梯制定了打散策略,空闲电梯运动方向受到来自于其余电梯,与电梯容量、距离相关的压力的影响,使电梯容量倾向于在电梯运行空间内均匀分布,以减小调度时间指望。同时第三次做业不一样种类的电梯制定了单独的策略,一个重要的策略是减小A类电梯空转时间。最终在强测中均得到了99分以上的成绩。
这两个方法还有很大的改进空间。这几回做业优化的主要路线都集中于如何应对将来可能的请求,而对当前已有的请求策略并非最优。第三次做业的请求分配也存在问题,可能与当前最优偏离较大,但在随机数据状况下偏离不太明显。另外一个优化路线是何泽欣同窗的基于模拟的估价函数,模拟不一样状态下电梯运行时间以完成评判。若是能够创建一个历史与将来的请求相结合的分配方法可能会有更好的效果。
实际上优化一个策略函数是一个比较复杂的问题,特别是针对将来的请求指望。或许能够有一些机器学习的方法,例如梯度降低法加以解决,完成对策略函数的拟合。
P.S.若是针对强测,考虑到助教、同窗之间的博弈,均衡应该是彻底随机的数据,这个也是打散算法较为重要的缘由。