第二次博客作业
前言
java混沌之源——多线程的名气早有耳闻,第一次写具有一定规模的多线程代码还是挺刺激的。
不过,由于之前长期考虑过多线程电梯的架构,这三次虽然写得很绝望,性能分也不算高,但多少
还是没被测出Bug,这一点实属幸运。
接下来就主要谈谈三次作业自己的一些设计思路以及优化思路,顺便谈谈自己搭评测姬遇到的一些坑点。
前排感谢 lsj、wsb、shh、xcb四位巨巨(排序由随机函数生成)
第五次作业
类图
这里,为了观看体验,我把方法以及变量隐去了。
Dispatcher
线程负责和电梯的控制器线程(Scheduler
)直接交互,Dispatcher
把Main
传入的请求分配给电梯的控制器。电梯的控制器周期性苏醒,检查自己的任务列表,对电梯发出指令。
此外,GlobalPermission
和DispatcherClose
是两个用于控制进程结束的对象。
时序图
Main方法输入端与Dispatcher的交互
Scheduler与Dispatcher的交互
电梯指令(三次电梯作业都沿用)
public enum ElevatorOrder {
GO_UP,GO_DOWN,STAY_IDLE,OPEN_DOOR,CLOSE_DOOR
}
Elevator
的工作机制是这样的,每一个最小时间周期苏醒一次,这代表上一个周期scheduler
给电梯下达的指令,电梯已经完成。此时电梯需要向电梯发送自己最新的状态报告,之后根据自身已得到的任务作出下一个周期的指令,并进入执行状态(sleep)
Dispatcher
通过blockqueue
来查询电梯的状态,即,只有每一个电梯都给分配器发送了自己最新状态的报告,分配器才会认为此时得到的电梯信息足够新鲜,可以作为任务分配依据,才会给各个电梯进行任务分配。
后来在实践中,笔者认识到,这个架构极其 愚蠢 ,原因如下
- 在电梯发送自身报告之后,会立刻去检查已经被分配给自己的任务,这就可能出现,
dispatcher
收集到足够信息刚刚要分配任务时,电梯已经完成了任务检查,那么,这个报告机制失去了它的意义。 - 这个“报告”机制导致我需要在线程结束的顺序上做出更多的工作,如果电梯线程先于
dispatcher
线程结束,那么,dispatcher
就可能陷入死等报告的状态,无法结束。这要求,我们必须保证dispatcher
线程先于电梯线程结束,这就是为什么,我的设计里有一个突兀的DispatcherClose
类。
代码分析报告
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
elevator.Dispatcher.Dispatcher(int,ArrayList<>,Permission,Close) | 1 | 1 | 1 |
elevator.Dispatcher.dispatch() | 1 | 2 | 2 |
elevator.Dispatcher.getNewRequests() | 1 | 1 | 1 |
elevator.Dispatcher.hasWork() | 1 | 2 | 2 |
elevator.Dispatcher.run() | 1 | 4 | 4 |
elevator.Dispatcher.updateReports() | 1 | 2 | 2 |
elevator.Dispatcher.updateRequests() | 1 | 1 | 1 |
elevator.DispatcherClose.close() | 1 | 1 | 1 |
elevator.DispatcherClose.isClosed() | 1 | 1 | 1 |
elevator.Elevator.Elevator(int) | 1 | 1 | 1 |
elevator.Elevator.executeOrder() | 12 | 11 | 16 |
elevator.Elevator.getRecord() | 1 | 1 | 1 |
elevator.Elevator.getState() | 1 | 1 | 1 |
elevator.Elevator.isInteger(BigDecimal) | 1 | 1 | 1 |
elevator.Elevator.setOrder(ElevatorOrder) | 1 | 1 | 1 |
elevator.ElevatorRecord.ElevatorRecord(int,BigDecimal) | 1 | 1 | 1 |
elevator.ElevatorRecord.getMileage() | 1 | 1 | 1 |
elevator.ElevatorState.ElevatorState(BigDecimal,boolean,ElevatorOrder) | 1 | 1 | 1 |
elevator.ElevatorState.getDoorOpen() | 1 | 1 | 1 |
elevator.ElevatorState.getFloor() | 1 | 1 | 1 |
elevator.ElevatorState.getToDo() | 1 | 1 | 1 |
elevator.ElevatorState.toString() | 1 | 1 | 1 |
elevator.GlobalPermission.forbid() | 1 | 1 | 1 |
elevator.GlobalPermission.getPermitted() | 1 | 1 | 1 |
elevator.Main.main(String[]) | 3 | 4 | 4 |
elevator.Scheduler.Scheduler(int,GlobalPermission,DispatcherClose) | 1 | 1 | 1 |
elevator.Scheduler.getDispatchedRequests() | 1 | 1 | 1 |
elevator.Scheduler.getNewRequests() | 1 | 1 | 1 |
elevator.Scheduler.getReportBox() | 1 | 1 | 1 |
elevator.Scheduler.hasWork() | 1 | 3 | 3 |
elevator.Scheduler.meetRequest() | 1 | 3 | 4 |
elevator.Scheduler.pickRequest() | 1 | 3 | 4 |
elevator.Scheduler.run() | 1 | 5 | 5 |
elevator.Scheduler.schedule() | 6 | 4 | 6 |
elevator.Scheduler.sendOrder(ElevatorOrder) | 1 | 1 | 1 |
elevator.Scheduler.sendReport() | 1 | 1 | 1 |
elevator.Sleeper.sleep(long) | 1 | 3 | 3 |
Class | OCavg | WMC | |
elevator.Dispatcher | 1.43 | 10 | |
elevator.DispatcherClose | 1 | 2 | |
elevator.Elevator | 3 | 18 | |
elevator.ElevatorOrder | n/a | 0 | |
elevator.ElevatorRecord | 1 | 2 | |
elevator.ElevatorState | 1 | 5 | |
elevator.GlobalPermission | 1 | 2 | |
elevator.Main | 3 | 3 | |
elevator.Scheduler | 1.91 | 21 | |
elevator.Sleeper | 3 | 3 | |
Package | v(G)avg | v(G)tot | |
elevator | 2.16 | 80 | |
Module | v(G)avg | v(G)tot | |
P5 | 2.16 | 80 | |
Project | v(G)avg | v(G)tot | |
project | 2.16 | 80 |
可以看出,笔者的这次,因为业务逻辑较为简单,没有出现业务逻辑过于密集的类,但是因为对于电梯控制缺乏经验,与产生电梯指令,执行电梯指令相关的
elevator.Scheduler.schedule()
和elevator.Elevator.executeOrder()
两个方法的复杂度有点过高。
关于SOLID原则
本次作业功能较为简单,本人的重点放在了线程协作架构的可拓展性上了,对于功能的拓展并未做过多深入,故无法提现SOLID原则,其中由于Shceduler
类完成了发送报告的工作,违反了SPR原则。
测试
本次的测试不论是自己测自己还是测别人都与前几次作业有很大不同,关于评测姬的搭建,笔者将其放到文末,此处仅仅简要报告测试情况。
自测
本人在自测阶段发现了自己前文所述如果线程退出顺序不对,出现无法结束程序的问题。其它问题,并没有出现。
强测 & 互测
由于业务逻辑简单,故大家都开心无伤过。
顺便%一下shh的23行单线程解决问题的代码,orz
第六次作业
类图
本次作业由于引入了地下楼层这个神奇的机制,于是我依据wsb巨巨的建议设置了楼层类,并通过轨道类来管理楼层类。电梯以及其控制器保存同一个Rail(轨道)对象,用于实现与楼层编号相关的计算与转换。此外,这次设计放弃了发送报告的机制,采用ElevatorStatusBuffer
作为电梯把自己信息提供给Dispatcher
的中转站。
时序图
输入端与Dispatcher
的交互
Dispatcher
与Elevator
的交互
本次我的线程写作关系受到了OS课程的启发
以下文段出自OS指导书
用户态和内核态的概念相信大家也不陌生,内核态即计组实验中所提到的特权态,用户态就是非特权态。mips 汇编中使用一些特权指令如mtc0、mfc0、syscall等都会陷入特权态(内核态)。
而我们这次实验,根据./include/mmu.h里面的布局来说,我们是2G/2G 模式,用户态占用2G,内核态占用2G。接下来,也是最容易产生混淆的地方,进程从用户态提升到内核态的过程,操作系统中发生了什么?
是从当前进程切换成一个所谓的“内核进程”来管理一切了吗?不!还是一样的配方,还是一样的进程!改变的其实只是进程的权限!我们打一个比方,你所在的班级里没有班长,任何人都可以以合理的理由到老师那申请当临时班长。你是班里的成员吗?当然是的。某天你申请当临时班长,申请成功了,那么现在你还是班里的成员吗?当然还是的。那么你前后有什么区别呢?是权限的变化。可能你之前和其他成员是完全平等,互不干涉的。那么现在你可以根据花名册点名,你可以安排班里的成员做些事情,你可以开班长会议等等。那么我们能说你是班长吗?不能,因为你并不是永久的班长。但能说你拥有成为班长的资格吗?当然可以,这种成为临时班长的资格,我们可以粗略地认为它就是内核态的精髓所在。
而在操作系统中,每个完整的进程都拥有这种成为临时内核的资格,即所有的进程都可以发出申请变成内核态下运行的进程。内核态下进程可访问的资源更多,更加自由。在之后我们会提到一种“申请”的方式,就叫做“系统调用”。
我把Dispatcher
视为内核,只有一个线程抢到了内核权限,它才能够更新全部电梯在Dispatcher
记录的状态,并给各个电梯(包括非“内核态”下的自己)分配任务。
现在看来,我的这个设计有一个问题,如果此时有多部电梯,那么电梯A退出内核态到更新ElevatorStatusBuffer
的时间间隙另一个电梯B进入内核态开始查询状态,可能会出现在B分配任务的时候,利用的是一个已经过时的状态,这个问题虽然不一定影响正确性但在某些边界条件下可能会影响性能。
代码质量报告
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
ElevatorSystem.main(String[]) | 3 | 5 | 5 |
dispatcher.Dispatcher.Dispatcher(Rail,GlobalPermission) | 1 | 1 | 1 |
dispatcher.Dispatcher.addAlsElevator() | 1 | 1 | 1 |
dispatcher.Dispatcher.addLookElevator() | 1 | 1 | 1 |
dispatcher.Dispatcher.addNaiveElevator() | 1 | 1 | 1 |
dispatcher.Dispatcher.addSillyElevator() | 1 | 1 | 1 |
dispatcher.Dispatcher.dispatchRequests() | 1 | 4 | 4 |
dispatcher.Dispatcher.getBuffer() | 1 | 1 | 1 |
dispatcher.Dispatcher.getElevators() | 1 | 1 | 1 |
dispatcher.Dispatcher.hasMoreWork() | 1 | 3 | 3 |
dispatcher.Dispatcher.setElevators(ArrayList |
1 | 1 | 1 |
dispatcher.Dispatcher.updateRequests() | 1 | 1 | 1 |
elevator.Elevator.Elevator(int,Rail,SchedulerType,Dispatcher) | 2 | 2 | 6 |
elevator.Elevator.closeDoor() | 1 | 2 | 2 |
elevator.Elevator.executeOrder(ElevatorOrder) | 2 | 7 | 7 |
elevator.Elevator.getDispatcher() | 1 | 1 | 1 |
elevator.Elevator.getHelper() | 1 | 1 | 1 |
elevator.Elevator.getOrderAndUpdateBuffer() | 1 | 1 | 1 |
elevator.Elevator.getRail() | 1 | 1 | 1 |
elevator.Elevator.getScheduler() | 1 | 1 | 1 |
elevator.Elevator.getStatus() | 1 | 1 | 1 |
elevator.Elevator.getStatusBuffer() | 1 | 1 | 1 |
elevator.Elevator.hasMoreWork() | 1 | 1 | 1 |
elevator.Elevator.openDoor() | 1 | 2 | 2 |
elevator.Elevator.run() | 1 | 2 | 2 |
elevator.Elevator.servePassengers() | 1 | 2 | 2 |
elevator.Elevator.updateStatusBuffer(ElevatorOrder) | 2 | 2 | 6 |
elevator.ElevatorFactory.ElevatorFactory(Rail,Dispatcher) | 1 | 1 | 1 |
elevator.ElevatorFactory.produceAlsElevator() | 1 | 1 | 1 |
elevator.ElevatorFactory.produceLookElevator() | 1 | 1 | 1 |
elevator.ElevatorFactory.produceNaiveElevator() | 1 | 1 | 1 |
elevator.ElevatorFactory.produceSillyElevator() | 1 | 1 | 1 |
elevator.ElevatorStatus.ElevatorStatus(int,int,int,boolean,ElevatorOrder) | 1 | 1 | 1 |
elevator.ElevatorStatus.getExecutingOrder() | 1 | 1 | 1 |
elevator.ElevatorStatus.getFloorIndex() | 1 | 1 | 1 |
elevator.ElevatorStatus.getMileage() | 1 | 1 | 1 |
elevator.ElevatorStatusBuffer.ElevatorStatusBuffer(ElevatorStatus) | 1 | 1 | 1 |
elevator.ElevatorStatusBuffer.getStatus() | 1 | 1 | 1 |
elevator.ElevatorStatusBuffer.setStatus(ElevatorStatus) | 1 | 1 | 1 |
exception.FloorIdException.FloorIdException(int) | 1 | 1 | 1 |
floor.Floor.Floor(int) | 1 | 1 | 1 |
floor.Floor.getId() | 1 | 1 | 1 |
floor.Rail.Rail(int,int) | 1 | 1 | 3 |
floor.Rail.getFloorId(int) | 1 | 1 | 1 |
floor.Rail.getFromFloorIndex(PersonRequest) | 1 | 2 | 2 |
floor.Rail.getIndex(int) | 3 | 1 | 5 |
floor.Rail.getToFloorIndex(PersonRequest) | 1 | 2 | 2 |
scheduler.AlsSchedule.AlsSchedule(Elevator) | 1 | 1 | 1 |
scheduler.AlsSchedule.canGetOut(MarkRequest) | 2 | 2 | 3 |
scheduler.AlsSchedule.canPick(MarkRequest) | 3 | 3 | 5 |
scheduler.AlsSchedule.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
scheduler.AlsSchedule.getOrder() | 7 | 2 | 7 |
scheduler.AlsSchedule.hasMoreWork() | 1 | 3 | 3 |
scheduler.AlsSchedule.isNeedServe() | 5 | 3 | 5 |
scheduler.AlsSchedule.meetPassengers() | 1 | 3 | 4 |
scheduler.AlsSchedule.passengersServe() | 1 | 1 | 1 |
scheduler.AlsSchedule.pickPassengers() | 1 | 3 | 3 |
scheduler.AlsSchedule.toString() | 1 | 1 | 1 |
scheduler.AlsSchedule.updateMainRequest() | 2 | 3 | 4 |
scheduler.LookScheduler.LookScheduler(Elevator) | 1 | 1 | 1 |
scheduler.LookScheduler.canGetOut(MarkRequest) | 2 | 2 | 3 |
scheduler.LookScheduler.canPick(MarkRequest) | 8 | 10 | 16 |
scheduler.LookScheduler.cntNeedDown() | 1 | 2 | 3 |
scheduler.LookScheduler.cntNeedUp() | 1 | 2 | 3 |
scheduler.LookScheduler.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
scheduler.LookScheduler.getOrder() | 12 | 2 | 12 |
scheduler.LookScheduler.hasMoreWork() | 1 | 2 | 2 |
scheduler.LookScheduler.isNeedServe() | 5 | 3 | 5 |
scheduler.LookScheduler.passengersServe() | 1 | 4 | 4 |
scheduler.LookScheduler.toString() | 1 | 1 | 1 |
scheduler.MarkRequest.MarkRequest(Rail,PersonRequest) | 1 | 1 | 3 |
scheduler.MarkRequest.equals(Object) | 3 | 1 | 3 |
scheduler.MarkRequest.getDirection() | 1 | 1 | 1 |
scheduler.MarkRequest.getFromFloorIndex() | 1 | 1 | 1 |
scheduler.MarkRequest.getNowNeedDirection(int) | 3 | 1 | 3 |
scheduler.MarkRequest.getPersonId() | 1 | 1 | 1 |
scheduler.MarkRequest.getPersonRequest() | 1 | 1 | 1 |
scheduler.MarkRequest.getTargetIndex() | 2 | 2 | 2 |
scheduler.MarkRequest.getToFloorIndex() | 1 | 1 | 1 |
scheduler.MarkRequest.isPicked() | 1 | 1 | 1 |
scheduler.MarkRequest.pick() | 1 | 1 | 1 |
scheduler.NaiveScheduler.NaiveScheduler(Elevator) | 1 | 1 | 1 |
scheduler.NaiveScheduler.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
scheduler.NaiveScheduler.getOrder() | 7 | 3 | 7 |
scheduler.NaiveScheduler.hasMoreWork() | 1 | 3 | 3 |
scheduler.NaiveScheduler.meetRequests() | 1 | 3 | 3 |
scheduler.NaiveScheduler.passengersServe() | 1 | 1 | 1 |
scheduler.NaiveScheduler.pickPassengers() | 1 | 3 | 3 |
scheduler.NaiveScheduler.toString() | 1 | 1 | 1 |
scheduler.RunTarget.RunTarget(int,Direction) | 1 | 1 | 1 |
scheduler.RunTarget.getRunDirection() | 1 | 1 | 1 |
scheduler.RunTarget.getTargetIndex() | 1 | 1 | 1 |
scheduler.RunTarget.toString() | 1 | 1 | 1 |
scheduler.SillyScheduler.SillyScheduler(Elevator) | 1 | 1 | 1 |
scheduler.SillyScheduler.canGetOut(MarkRequest) | 2 | 2 | 3 |
scheduler.SillyScheduler.canPick(MarkRequest) | 7 | 5 | 10 |
scheduler.SillyScheduler.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
scheduler.SillyScheduler.getDistance(MarkRequest) | 1 | 1 | 1 |
scheduler.SillyScheduler.getOrder() | 5 | 1 | 5 |
scheduler.SillyScheduler.getRunDirection(MarkRequest) | 3 | 1 | 3 |
scheduler.SillyScheduler.hasMoreWork() | 1 | 2 | 2 |
scheduler.SillyScheduler.isNeedServe() | 5 | 3 | 5 |
scheduler.SillyScheduler.passengersServe() | 1 | 5 | 6 |
scheduler.SillyScheduler.selectMaintarget(int,int,int,int,int) | 8 | 8 | 12 |
scheduler.SillyScheduler.setMainTarget(int,Direction) | 1 | 1 | 1 |
scheduler.SillyScheduler.toString() | 1 | 1 | 1 |
scheduler.SillyScheduler.updateMainTarget() | 5 | 4 | 7 |
tools.GlobalPermission.isSystemContinue() | 1 | 1 | 1 |
tools.GlobalPermission.systemQuit() | 1 | 1 | 1 |
tools.GlobalPermission.systemStart() | 1 | 1 | 1 |
tools.OutputHelper.finalOutput() | 1 | 2 | 2 |
tools.OutputHelper.output(OutputHelper) | 1 | 2 | 2 |
tools.OutputHelper.println(String) | 1 | 1 | 1 |
tools.TimeManager.idleSleep() | 1 | 2 | 3 |
tools.TimeManager.moveSleep() | 1 | 2 | 3 |
tools.TimeManager.serveSleep() | 1 | 2 | 3 |
Class | OCavg | WMC | |
ElevatorSystem | 3 | 3 | |
dispatcher.Dispatcher | 1.18 | 13 | |
elevator.Elevator | 2.2 | 33 | |
elevator.ElevatorFactory | 1 | 5 | |
elevator.ElevatorOrder | n/a | 0 | |
elevator.ElevatorStatus | 1 | 4 | |
elevator.ElevatorStatusBuffer | 1 | 3 | |
exception.FloorIdException | 1 | 1 | |
floor.Floor | 1 | 2 | |
floor.Rail | 1.8 | 9 | |
scheduler.AlsSchedule | 2.75 | 33 | |
scheduler.LookScheduler | 3.73 | 41 | |
scheduler.MarkRequest | 1.64 | 18 | |
scheduler.NaiveScheduler | 2.25 | 18 | |
scheduler.RunTarget | 1 | 4 | |
scheduler.SchedulerType | n/a | 0 | |
scheduler.SillyScheduler | 3.57 | 50 | |
tools.Direction | n/a | 0 | |
tools.GlobalPermission | 1 | 3 | |
tools.OutputHelper | 1.67 | 5 | |
tools.TimeManager | 2 | 6 | |
Package | v(G)avg | v(G)tot | |
5 | 5 | ||
dispatcher | 1.45 | 16 | |
elevator | 1.74 | 47 | |
exception | 1 | 1 | |
floor | 2.14 | 15 | |
scheduler | 3.15 | 189 | |
tools | 1.89 | 17 | |
Module | v(G)avg | v(G)tot | |
Project6 | 2.5 | 290 | |
Project | v(G)avg | v(G)tot | |
project | 2.5 | 290 |
经过分析可以发现,方法的复杂性依旧体现在调度算法的实现上,这一类方法始终是复杂度最高的方法。
此外此次作业中电梯类的设计耦合度过高,主要是因为我在设计构造方法的时候欠考虑了,导致电梯的构造与管理非常混乱。
关于SOLID原则
-
单一责任原则:
删去了
Scheduler
的发送报告方法,使其功能更加单一——根据请求与电梯状态控制电梯行为 -
开放封闭原则
我的
Rail
,Floor
支持扩展,且不必修改任何原有的功能方法,并且在第三次作业中得到了应用,这两个类符合开闭原则。 -
里氏替换原则
本次设计本人未使用继承操作,故不涉及。
-
依赖倒置原则
本次作业本人采用了多种电梯的控制策略,都采用了
Scheduler
这一接口,同时他们的实现各有特点。 -
接口分离原则
这次的设计仅仅使用了
Scheduler
这唯一的接口,且都是有必要的方法,故不涉及。
性能优化
关于测试
我方Bug
本人这次的Look
算法有很严重的Bug:如果此时电梯处于Idle状态,且上下同时各自来一个请求,且无后续请求,那么电梯将卡死不动。但是,这个Bug被我的奇技淫巧优化给巧妙地回避了。所以在强测和互测都没有翻车。
对方Bug
这次同屋神仙有丶多,代码风格好看,命名规范,算法强大,鄙人才学疏浅实在找不出问题。
第七次作业
类图
本次作业中,我给Floor
加入了访问权限这一设定,每一部电梯通过自己私有的Rail
可以查询自己是否有权限在当前楼层停靠。为了便于实现换乘的功能,我引入了TemRequest
这个概念(为了保证类图的简洁直观,并未在图中体现),这类对象的作用在于,划分行动路径,把难以一步到位的请求分割成多个可一步到位的请求,把这些可以一步到位的请求称之为TemRequest
。本次设计中,我引入了Person
类,其保存了乘客的楼层位置信息
、TemRequest
、请求是否分配给电梯
等信息。前文提到,我们需要实现对请求
的分割,RequestDivider
所完成的即是这一工作。至于识别请求
可否一步到位,这个任务交给了RegionJudger
来完成。其余部分与第六次作业设计类似,故不赘述。
时序图
输入端与Dispatcher
的交互
Dispatcher
与Elevator
的交互
本次设计我沿用了第六次作业的内核态模式,不过更加“集权”。此次设计中,各个线程在没有Dispatcher
权限的时候,甚至无法给自己下达指令,无法改变自己的状态。这么设计看似有丶违背常理,但是这么做有一个好处,我可以保证线程在Dispatcher
权限下查询到的各个电梯的信息,在完成此次任务分配时都一定时有效的信息,即,与此时各个电梯的状态完全一致。
代码分析报告
代码复杂度
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
ElevatorSystem.main(String[]) | 3 | 6 | 6 |
algorithm.Allocater.Allocater(RegionJudger) | 1 | 1 | 1 |
algorithm.Allocater.canByTheWay(int,Direction,PersonRequest) | 3 | 3 | 7 |
algorithm.Allocater.selectAb(PersonRequest) | 4 | 2 | 6 |
algorithm.Allocater.selectAbc(PersonRequest) | 11 | 1 | 20 |
algorithm.Allocater.selectBc(PersonRequest) | 6 | 3 | 6 |
algorithm.Allocater.selectElevatorAllocate(Person,Status,Status,Status) | 8 | 5 | 8 |
algorithm.Allocater.updateValue(Request,Status,Status,Status) | 1 | 1 | 1 |
algorithm.RegionJudger.ACanSolo(PersonRequest) | 1 | 2 | 2 |
algorithm.RegionJudger.BCanSolo(PersonRequest) | 1 | 2 | 2 |
algorithm.RegionJudger.CCanSolo(PersonRequest) | 1 | 2 | 2 |
algorithm.RegionJudger.RegionJudger(Rail,Rail,Rail) | 1 | 1 | 1 |
algorithm.RegionJudger.canSolo(PersonRequest) | 1 | 3 | 3 |
algorithm.RegionJudger.floorIndexInAHighRegion(int) | 1 | 2 | 2 |
algorithm.RegionJudger.floorIndexInALowRegion(int) | 1 | 1 | 2 |
algorithm.RegionJudger.fromARegion(PersonRequest) | 1 | 1 | 1 |
algorithm.RegionJudger.fromBRegion(PersonRequest) | 1 | 1 | 1 |
algorithm.RegionJudger.fromCRegion(PersonRequest) | 1 | 1 | 1 |
algorithm.RegionJudger.getCanTakeMode(PersonRequest) | 6 | 5 | 10 |
algorithm.RegionJudger.inHighRegionA(PersonRequest) | 1 | 2 | 2 |
algorithm.RegionJudger.inLowRegionA(PersonRequest) | 1 | 2 | 2 |
algorithm.RegionJudger.onlyACanSolo(PersonRequest) | 1 | 3 | 3 |
algorithm.RegionJudger.toARegion(PersonRequest) | 1 | 1 | 1 |
algorithm.RegionJudger.toBRegion(PersonRequest) | 1 | 1 | 1 |
algorithm.RegionJudger.toCRegion(PersonRequest) | 1 | 1 | 1 |
algorithm.RequestDivider.RequestDivider(Rail,Rail,Rail,RegionJudger) | 1 | 1 | 1 |
algorithm.RequestDivider.setPersonTemRequest(Person) | 14 | 15 | 16 |
algorithm.RequestDivider.setPersonTemRequest1(Person) | 13 | 12 | 13 |
algorithm.RequestDivider.setTemRequest(Person,int,int) | 1 | 1 | 1 |
dispatcher.Dispatcher.Dispatcher(GlobalPermission) | 1 | 1 | 1 |
dispatcher.Dispatcher.allocatePerson() | 1 | 5 | 5 |
dispatcher.Dispatcher.allocatePersonToElevator(Person,int) | 1 | 1 | 3 |
dispatcher.Dispatcher.checkPersonArriveDestination() | 4 | 2 | 4 |
dispatcher.Dispatcher.dispatch() | 1 | 1 | 1 |
dispatcher.Dispatcher.getBuffer() | 1 | 1 | 1 |
dispatcher.Dispatcher.getElevators() | 1 | 1 | 1 |
dispatcher.Dispatcher.getNewInputRequests() | 1 | 2 | 2 |
dispatcher.Dispatcher.getPersonById(int) | 3 | 2 | 3 |
dispatcher.Dispatcher.hasMoreWork() | 1 | 3 | 3 |
dispatcher.Dispatcher.passengersIn(ArrayList |
1 | 2 | 2 |
dispatcher.Dispatcher.passengersOut(ArrayList |
1 | 2 | 2 |
dispatcher.Dispatcher.personIn(Person,int,int) | 1 | 1 | 3 |
dispatcher.Dispatcher.personOut(Person,int) | 1 | 1 | 1 |
dispatcher.Dispatcher.update() | 1 | 1 | 1 |
dispatcher.Dispatcher.updateElevatorStatus() | 1 | 1 | 1 |
dispatcher.Dispatcher.updatePersonFloor() | 2 | 6 | 7 |
dispatcher.Dispatcher.updateRequestsDivision() | 1 | 2 | 2 |
elevator.Elevator.Elevator(String,int,Rail,Dispatcher,Timer,Type) | 2 | 2 | 5 |
elevator.Elevator.closeDoor() | 1 | 2 | 2 |
elevator.Elevator.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
elevator.Elevator.executeOrder(ElevatorOrder) | 2 | 5 | 5 |
elevator.Elevator.getCapacity() | 1 | 1 | 1 |
elevator.Elevator.getDispatcher() | 1 | 1 | 1 |
elevator.Elevator.getName() | 1 | 1 | 1 |
elevator.Elevator.getOrderAndPresetStatus() | 2 | 3 | 6 |
elevator.Elevator.getRail() | 1 | 1 | 1 |
elevator.Elevator.getStatus() | 1 | 1 | 1 |
elevator.Elevator.isNeedService() | 1 | 1 | 1 |
elevator.Elevator.notServing() | 1 | 1 | 1 |
elevator.Elevator.openDoor() | 1 | 2 | 2 |
elevator.Elevator.run() | 1 | 2 | 2 |
elevator.Elevator.servePassengers() | 1 | 1 | 1 |
elevator.Elevator.willServing() | 1 | 1 | 1 |
elevator.ElevatorStatus.ElevatorStatus(int,int,int,int,int,Direction) | 1 | 1 | 1 |
elevator.ElevatorStatus.getAllocatedNum() | 1 | 1 | 1 |
elevator.ElevatorStatus.getCapacity() | 1 | 1 | 1 |
elevator.ElevatorStatus.getCarryNum() | 1 | 1 | 1 |
elevator.ElevatorStatus.getDirection() | 1 | 1 | 1 |
elevator.ElevatorStatus.getFloorIndex() | 1 | 1 | 1 |
elevator.ElevatorStatus.getId() | 1 | 1 | 1 |
elevator.ElevatorStatus.isFull() | 1 | 1 | 1 |
exception.FloorIdException.FloorIdException(int) | 1 | 1 | 1 |
floor.Floor.Floor(int,boolean) | 1 | 1 | 1 |
floor.Floor.getId() | 1 | 1 | 1 |
floor.Floor.isCanMooring() | 1 | 1 | 1 |
floor.Floor.setPermission(boolean) | 1 | 1 | 1 |
floor.Rail.Rail(int,int) | 1 | 1 | 3 |
floor.Rail.Rail(int,int,int[]) | 1 | 2 | 4 |
floor.Rail.canMooringById(int) | 1 | 1 | 1 |
floor.Rail.canMooringByIndex(int) | 1 | 1 | 1 |
floor.Rail.getFloorId(int) | 1 | 1 | 1 |
floor.Rail.getFloorsSize() | 1 | 1 | 1 |
floor.Rail.getFromFloorIndex(PersonRequest) | 1 | 2 | 2 |
floor.Rail.getIndex(int) | 3 | 4 | 6 |
floor.Rail.getToFloorIndex(PersonRequest) | 1 | 2 | 2 |
person.Person.Person(PersonRequest) | 1 | 1 | 1 |
person.Person.arriveTotalTarget() | 1 | 1 | 1 |
person.Person.getAllocateStatus() | 1 | 1 | 1 |
person.Person.getId() | 1 | 1 | 1 |
person.Person.getLocationId() | 1 | 1 | 1 |
person.Person.getLocationIndex() | 1 | 1 | 1 |
person.Person.getState() | 1 | 1 | 1 |
person.Person.getTargetFloorId() | 1 | 1 | 1 |
person.Person.getTargetFloorIndex() | 1 | 1 | 1 |
person.Person.getTemRequest() | 1 | 1 | 1 |
person.Person.getTotalRequest() | 1 | 1 | 1 |
person.Person.isInA() | 1 | 1 | 1 |
person.Person.isInB() | 1 | 1 | 1 |
person.Person.isInC() | 1 | 1 | 1 |
person.Person.isOutElevator() | 1 | 1 | 1 |
person.Person.setAllocateStatus(AllocateStatus) | 1 | 1 | 1 |
person.Person.setLocationById(int) | 1 | 1 | 1 |
person.Person.setLocationByIndex(int) | 1 | 1 | 1 |
person.Person.setState(PersonState) | 1 | 1 | 1 |
person.Person.setTemRequest(PersonRequest) | 1 | 1 | 1 |
person.Person.toString() | 1 | 1 | 1 |
person.PersonMoveOrder.PersonMoveOrder(MoveOrderType,int) | 1 | 1 | 1 |
person.PersonMoveOrder.getPersonId() | 1 | 1 | 1 |
person.PersonMoveOrder.getType() | 1 | 1 | 1 |
scheduler.MarkRequest.MarkRequest(Rail,PersonRequest) | 1 | 1 | 3 |
scheduler.MarkRequest.equals(Object) | 3 | 1 | 3 |
scheduler.MarkRequest.getDirection() | 1 | 1 | 1 |
scheduler.MarkRequest.getFromFloorIndex() | 1 | 1 | 1 |
scheduler.MarkRequest.getNowNeedDirection(int) | 3 | 1 | 3 |
scheduler.MarkRequest.getPersonId() | 1 | 1 | 1 |
scheduler.MarkRequest.getPersonRequest() | 1 | 1 | 1 |
scheduler.MarkRequest.getTargetIndex() | 2 | 2 | 2 |
scheduler.MarkRequest.getToFloorIndex() | 1 | 1 | 1 |
scheduler.MarkRequest.isPicked() | 1 | 1 | 1 |
scheduler.MarkRequest.pick() | 1 | 1 | 1 |
scheduler.SchedulerForA.SchedulerForA(Elevator) | 1 | 1 | 1 |
scheduler.SchedulerForA.canGetOut(MarkRequest) | 2 | 2 | 3 |
scheduler.SchedulerForA.canPick(MarkRequest) | 10 | 10 | 18 |
scheduler.SchedulerForA.cntNeedDown() | 1 | 4 | 5 |
scheduler.SchedulerForA.cntNeedUp() | 1 | 4 | 5 |
scheduler.SchedulerForA.defaultMode() | 3 | 1 | 3 |
scheduler.SchedulerForA.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
scheduler.SchedulerForA.getCarryNum() | 1 | 1 | 1 |
scheduler.SchedulerForA.getDirection() | 1 | 1 | 1 |
scheduler.SchedulerForA.getOrder() | 18 | 19 | 20 |
scheduler.SchedulerForA.getRequestsNum() | 1 | 1 | 1 |
scheduler.SchedulerForA.isFull() | 1 | 1 | 1 |
scheduler.SchedulerForA.isNeedServe() | 1 | 4 | 4 |
scheduler.SchedulerForA.needGetIn() | 3 | 2 | 3 |
scheduler.SchedulerForA.needGetOut() | 3 | 2 | 3 |
scheduler.SchedulerForA.needReAllocated() | 6 | 5 | 12 |
scheduler.SchedulerForA.passengersGetIn() | 1 | 5 | 5 |
scheduler.SchedulerForA.passengersGetOut() | 1 | 3 | 3 |
scheduler.SchedulerForA.toString() | 1 | 1 | 1 |
scheduler.SchedulerForA.updateMainDirection(Direction) | 3 | 1 | 3 |
scheduler.SchedulerForB.SchedulerForB(Elevator) | 1 | 1 | 1 |
scheduler.SchedulerForB.canGetOut(MarkRequest) | 2 | 2 | 3 |
scheduler.SchedulerForB.canPick(MarkRequest) | 10 | 10 | 18 |
scheduler.SchedulerForB.cntNeedDown() | 1 | 4 | 5 |
scheduler.SchedulerForB.cntNeedUp() | 1 | 4 | 5 |
scheduler.SchedulerForB.defaultMode() | 3 | 1 | 3 |
scheduler.SchedulerForB.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
scheduler.SchedulerForB.getCarryNum() | 1 | 1 | 1 |
scheduler.SchedulerForB.getDirection() | 1 | 1 | 1 |
scheduler.SchedulerForB.getOrder() | 15 | 14 | 15 |
scheduler.SchedulerForB.getRequestsNum() | 1 | 1 | 1 |
scheduler.SchedulerForB.isFull() | 1 | 1 | 1 |
scheduler.SchedulerForB.isNeedServe() | 1 | 4 | 4 |
scheduler.SchedulerForB.needGetIn() | 3 | 2 | 3 |
scheduler.SchedulerForB.needGetOut() | 3 | 2 | 3 |
scheduler.SchedulerForB.needReAllocated() | 6 | 4 | 11 |
scheduler.SchedulerForB.passengersGetIn() | 1 | 5 | 5 |
scheduler.SchedulerForB.passengersGetOut() | 1 | 3 | 3 |
scheduler.SchedulerForB.toString() | 1 | 1 | 1 |
scheduler.SchedulerForB.updateMainDirection(Direction) | 3 | 1 | 3 |
scheduler.SchedulerForC.SchedulerForC(Elevator) | 1 | 1 | 1 |
scheduler.SchedulerForC.canGetOut(MarkRequest) | 2 | 2 | 3 |
scheduler.SchedulerForC.canPick(MarkRequest) | 10 | 10 | 18 |
scheduler.SchedulerForC.cntNeedDown() | 1 | 4 | 5 |
scheduler.SchedulerForC.cntNeedUp() | 1 | 4 | 5 |
scheduler.SchedulerForC.defaultMode() | 3 | 1 | 3 |
scheduler.SchedulerForC.dispatchRequest(PersonRequest) | 1 | 1 | 1 |
scheduler.SchedulerForC.getCarryNum() | 1 | 1 | 1 |
scheduler.SchedulerForC.getDirection() | 1 | 1 | 1 |
scheduler.SchedulerForC.getOrder() | 15 | 8 | 15 |
scheduler.SchedulerForC.getRequestsNum() | 1 | 1 | 1 |
scheduler.SchedulerForC.isFull() | 1 | 1 | 1 |
scheduler.SchedulerForC.isNeedServe() | 1 | 3 | 3 |
scheduler.SchedulerForC.needGetIn() | 3 | 2 | 3 |
scheduler.SchedulerForC.needGetOut() | 3 | 2 | 3 |
scheduler.SchedulerForC.passengersGetIn() | 1 | 5 | 5 |
scheduler.SchedulerForC.passengersGetOut() | 1 | 3 | 3 |
scheduler.SchedulerForC.toString() | 1 | 1 | 1 |
scheduler.SchedulerForC.updateMainDirection(Direction) | 3 | 1 | 3 |
tools.GlobalPermission.isSystemContinue() | 1 | 1 | 1 |
tools.GlobalPermission.systemQuit() | 1 | 1 | 1 |
tools.GlobalPermission.systemStart() | 1 | 1 | 1 |
tools.OutputHelper.OutputHelper() | 1 | 1 | 1 |
tools.OutputHelper.println(Object) | 1 | 1 | 1 |
tools.OutputHelper.println(boolean) | 1 | 1 | 1 |
tools.OutputHelper.println(char) | 1 | 1 | 1 |
tools.OutputHelper.println(char[]) | 1 | 1 | 1 |
tools.OutputHelper.println(double) | 1 | 1 | 1 |
tools.OutputHelper.println(float) | 1 | 1 | 1 |
tools.OutputHelper.println(int) | 1 | 1 | 1 |
tools.OutputHelper.println(long) | 1 | 1 | 1 |
tools.TimeManager.TimeManager(int,int,int) | 1 | 1 | 1 |
tools.TimeManager.idleSleep() | 1 | 2 | 2 |
tools.TimeManager.moveSleep() | 1 | 2 | 2 |
tools.TimeManager.serveSleep() | 1 | 2 | 2 |
Class | OCavg | WMC | |
ElevatorSystem | 4 | 4 | |
algorithm.AllocateStatus | n/a | 0 | |
algorithm.Allocater | 4.86 | 34 | |
algorithm.CanTakeMode | n/a | 0 | |
algorithm.RegionJudger | 1.29 | 22 | |
algorithm.RequestDivider | 7.25 | 29 | |
dispatcher.Dispatcher | 2.28 | 41 | |
elevator.Elevator | 2 | 32 | |
elevator.ElevatorOrder | n/a | 0 | |
elevator.ElevatorStatus | 1 | 8 | |
exception.FloorIdException | 1 | 1 | |
floor.Floor | 1 | 4 | |
floor.Rail | 1.78 | 16 | |
person.MoveOrderType | n/a | 0 | |
person.Person | 1 | 21 | |
person.PersonMoveOrder | 1 | 3 | |
person.PersonState | n/a | 0 | |
scheduler.MarkRequest | 1.64 | 18 | |
scheduler.SchedulerForA | 3.6 | 72 | |
scheduler.SchedulerForB | 3.35 | 67 | |
scheduler.SchedulerForC | 3.11 | 59 | |
scheduler.SchedulerType | n/a | 0 | |
tools.Direction | n/a | 0 | |
tools.GlobalPermission | 1 | 3 | |
tools.OutputHelper | 1 | 9 | |
tools.TimeManager | 1 | 4 | |
Package | v(G)avg | v(G)tot | |
6 | 6 | ||
algorithm | 4.18 | 117 | |
dispatcher | 2.39 | 43 | |
elevator | 1.67 | 40 | |
exception | 1 | 1 | |
floor | 1.92 | 25 | |
person | 1 | 24 | |
scheduler | 3.94 | 276 | |
tools | 1.19 | 19 | |
Module | v(G)avg | v(G)tot | |
Project7 | 2.83 | 551 | |
Project | v(G)avg | v(G)tot | |
project | 2.83 | 551 |
可见,本人这次的Scheduler
,Allocater
,RequestDivider
等几个类的复杂度过高,这次的电梯设计较为复杂,判断逻辑较为复杂,时间也比较紧,本人没来得及解决这个问题,实属遗憾。
关于SOLID原则
-
单一责任原则
本次设计中,我的各个类的职责分工还是比较明确的,比如
RegionJudger
,RequestDivider
,Dispatcher
,Rail
,Scheduler
几个类的协同工作,各司其职,井然有序,基本符合单一职责原则。 -
开放封闭原则
本次设计的开放封闭原则主要体现在
Rail
上,这个类本身仅仅是给电梯一个楼层编号计算的支持,以及判断是否具有停靠权限,且这是个不可变对象。此次设计中,这个判断是否具有停靠权限的功能就在不做任何修改的情况下直接应用于RegionJuder
的工作,体现了开放封闭原则。 -
里氏替换原则
本人此次设计又未使用继承机制,故不涉及此处。
-
依赖倒置原则
本次作业本人采用了多种电梯的控制策略,都采用了
Scheduler
这一接口,使其具有相同类型的业务功能。体现了依赖倒置原则 -
接口分离原则
这次的设计仅仅使用了
Scheduler
这唯一的接口,且都是有必要的方法,故不涉及。
性能优化
本次本人的优化主要使用的是wsb巨巨的优化策略,俗称“电踢”策略,来实现半动态规划。
测试
我方Bug
-
RegionJuder
考虑不周RegionJudger
的业务逻辑较为复杂,稍有不慎就会出现考虑不周的情况。本人在早期确实在此处有几个Bug。不过这种Bug比较容易发现,评测机的大量数据下,这种Bug基本都能暴露出来。 -
Look
算法出现的Bug当一个电梯处于
IDLE
状态,且此时上下同时各来一个请求且无其他请求,那么电梯将陷入永久保持IDLE的状态,这个Bug是中测样例查出来的。 -
B电梯在3楼开门
由于本人给B电梯加入了根据自身负载决定是否在奇数楼层踢人下去分配给C电梯的功能且没处理好3楼这个边界条件,故在评测机的扫射下测出了一组B在3层开门的错误,加上一个特判后,问题就解决了。
以上问题全部在提交截止前解决,所以本人在强测与互测中未被查出Bug。
对方Bug
这次我好像又一次误入了神仙屋,他们不仅仅没有被我查出Bug,性能还贼强,我太菜了,哭了。
评测姬遇到的几个问题与解决方案
-
输出时间戳与自己设定输入时间间的误差影响评测
解决方案:
1、根据第一条输出的时间与第一条输入的时间进行误差修正,同时放宽对时间的判断的限度,加入0.05s的允许误差。
2、使用hdl封装的接口,据说用这个可以基本消除这个误差,orz
-
管道被写满
之前的评测由于输出较短,一般不会出现这种情况,所以本人没有意识到这个问题,第七次作业中,本人给自己的代码对拍时,发现会各种TLE,后来发现是管道写满了,写入被阻塞,导致TLE
解决方案:
在不断轮训等待“输入时间窗口”的时候判断被测程序是否往管道中写入了信息,如果写入就读出保存。
在Java可以通过
ImputStream
的available()
方法判断管道内是否已经被写入了信息(如果被写入,那么available != 0)。 -
使用Java对调用输出流的
println
一类的方法后,待测程序没有接收到解决方案:
随手
flush
是个好文明。 -
遇到死锁的程序判处其TLE并防止它继续耗费资源
解决方案:
不断通过sleep轮训
process.isAlive()
与是否超过最大时限,如果超过最大时限(轮训结束,且process.isAlive()
为真)则判处TLE
,同时杀进程。关于杀进程
ubuntu16.04
:直接process.destroy()
可以把整个进程给杀干净。win10
:仅仅通过process.destroy
无法杀死整个进程,可以通过taskkill
来杀死超时的进程。 -
关于CPU时间的测试
解决方案:
水群里一位同学的方案,使用
time命令
可以查询CPU时间,本人尚未亲自尝试,下次窝窝作业可以一试。顺便,
time命令
好像win
没有诶,哭哭。
总结
- 之前的几次作业复杂度都不大,很多类的分工上的不合理并不会造成什么影响,但是到了第七次作业,各种相对复杂的问题扑面而来,原有的沙雕设计难以支持,笔者真正感受到了单一职责原则的强大。
- 笔者之前一直纠结于分配器线程的设计,学习了OS之后,我想到了可以采用这种类似于内核权限的设计方法,所以说,设计的灵感可以在学习的多个方面找到。
- 一个好的架构是优化的基础,比如第三次作业,我的内核态模式就较好地支持这种“电踢”半动态规划的优化方案,使得我心态没有完全爆炸