2022_OO_Unit2

2022_oo_Unit2

本单元作业为电梯调度,重点考察多线程编程。作业背景为初始有5栋楼,每栋楼10层。在每一楼层会存在乘客发来请求,请求会告知出发地和目的地,要求采用合理的调度方式,把每位乘客送至目的地。

第一次作业

要求:每栋楼各一部电梯,保证乘客不会发出跨楼座请求。

架构设计

架构和策略实现

采用生产者消费者模式,为5栋楼各设置一个controller用来处理发到该楼的请求,controller类维护一个elevator的list用以管理该栋楼的电梯。此时controller为生产者向ekevator发送请求,elevator为消费者消耗请求。设置一个RequestPool用以处理全部的输入,将输入的请求发送到相应controller,此时RequestPool为生产者,controller为消费者。对于具体的elevator而言,电梯采用的捎带策略为look。

UML图

image

UML协作图

image

线程安全控制

同步块设置为Controller中的requestQueue由controller和RequestQueue共享,Controller只读,RequestQueue只写。Elevator中的todo由Elevator和Controller共享,Elevator只读,Controller只写。

线程安全退出条件为,全部输入结束时RequestQueue结束,全部输入结束且Controller的requestQueue为空Controller结束, Controller结束且乘客请求处理完Elevator退出。

程序质量分析

度量分析

Controller 2.3333333333333335 5.0 7.0
Elevator 2.4375 8.0 39.0
Main 1.0 1.0 1.0
RequestPool 3.0 4.0 6.0
RequestQueue 1.0 1.0 7.0
SafeOutput 1.0 1.0 1.0
Total 61.0
Average 2.033333333333333 3.3333333333333335 10.166666666666666

所有类的复杂度都不算很高,Elevator类的method的复杂度偏高,是由于电梯的调度决策和行为全部由Elevator类实现,但个人认为Elevator的行为本身就应该由自身决定,不需要单独再为电梯运行策略封装一个类。

方法圈复杂度分析

Controller.Controller(String, int, RequestQueue) 0.0 1.0 1.0 1.0
Controller.run() 13.0 3.0 7.0 7.0
Controller.setAlive(boolean) 0.0 1.0 1.0 1.0
Elevator.add(PersonRequest) 0.0 1.0 1.0 1.0
Elevator.down() 1.0 1.0 2.0 2.0
Elevator.Elevator(String, int) 0.0 1.0 1.0 1.0
Elevator.getTodo() 0.0 1.0 1.0 1.0
Elevator.isPassengerIn() 4.0 3.0 3.0 4.0
Elevator.isPassengerOut() 3.0 3.0 2.0 3.0
Elevator.isTake(PersonRequest) 6.0 4.0 2.0 7.0
Elevator.look() 15.0 7.0 6.0 10.0
Elevator.openClose() 2.0 1.0 3.0 3.0
Elevator.passengersIn() 4.0 1.0 4.0 4.0
Elevator.passengersOut() 3.0 1.0 3.0 3.0
Elevator.response() 0.0 1.0 1.0 1.0
Elevator.run() 2.0 1.0 4.0 4.0
Elevator.running(int) 1.0 1.0 1.0 4.0
Elevator.setAlive(boolean) 0.0 1.0 1.0 1.0
Elevator.up() 1.0 1.0 2.0 2.0
Main.main(String[]) 0.0 1.0 1.0 1.0
RequestPool.RequestPool() 1.0 1.0 2.0 2.0
RequestPool.run() 8.0 3.0 4.0 5.0
RequestQueue.getController() 0.0 1.0 1.0 1.0
RequestQueue.getRequests() 0.0 1.0 1.0 1.0
RequestQueue.isEmpty() 0.0 1.0 1.0 1.0
RequestQueue.put(PersonRequest) 0.0 1.0 1.0 1.0
RequestQueue.RequestQueue() 0.0 1.0 1.0 1.0
RequestQueue.setController(Controller) 0.0 1.0 1.0 1.0
RequestQueue.take() 0.0 1.0 1.0 1.0
SafeOutput.println(String) 0.0 1.0 1.0 1.0
Total 64.0 47.0 61.0 76.0
Average 2.1333333333333333 1.5666666666666667 2.033333333333333 2.533333333333333

几乎全部的方法圈复杂度都不算高,说明函数的设计是合理的。

BUG分析

本次作业几乎不会出现bug,但对于线程安全输出还是存在bug。在阅读指导书时没有理解何为线程不安全的输出,由于System.out.println()本身是线程安全的,所以平常没有体会到这一点。解决方法是在Timeout.println()外加synchronized即可。

第二次作业

要求:增加了横向电梯,同楼栋可存在多部纵向电梯,可实现电梯的跨楼层操作。但是对于请求加上了较强的限制,只允许乘客在同一层楼或同一栋楼发出请求,不允许同时跨楼层和跨楼栋。

架构设计

架构和策略实现

沿用了上次的设计,由于本次作业横向电梯和纵向电梯没有交际,所以采用同样的方式对于横向电梯进行管理。设置一个controller维护floorElevator的list管理全部横向电梯。RequestQueue将请求分发给controller,controller再发给电梯。对于纵向电梯,采用controller调度给elevator分发请求,优先将请求发给可稍带的、较近的电梯,电梯无需处理请求调度和分发的工作。对于横向电梯也采用类似look的策略运行。

UML图

image

UML协作图

image

线程安全控制

同步块设置和上次基本一致,区别在于Controller会共享多个电梯的todo。

线程安全退出方式和上次相同,采用RequestPool--Controller--Elevator依次退出的方式结束程序。

程序质量分析

度量分析

BuildingController 1.5 4.0 9.0
BuildingElevator 2.3529411764705883 8.0 40.0
FloorController 3.0 9.0 12.0
FloorElevator 2.2777777777777777 8.0 41.0
Main 1.0 1.0 1.0
RequestPool 5.0 8.0 10.0
RequestQueue 1.0 1.0 9.0
SafeOutput 1.0 1.0 1.0
Total 123.0
Average 2.1206896551724137 5.0 15.375

类复杂度还是集中在Elevator类,原因同样是运行策略和电梯行为控制的方法均在Elevator中。而方法圈复杂度较高的RequestPool则是由于在处理输入是采用了过多的if条件判断以至于方法平均复杂度高,更好的设计是为每种请求实现专门的处理函数。

方法圈复杂度分析

BuildingController.addElevator(int) 0.0 1.0 1.0 1.0
BuildingController.BuildingController(String, int, RequestQueue) 0.0 1.0 1.0 1.0
BuildingController.getRequestQueue() 0.0 1.0 1.0 1.0
BuildingController.run() 7.0 3.0 5.0 5.0
BuildingController.setAlive(boolean) 0.0 1.0 1.0 1.0
BuildingController.setArgs(double, double, double, double) 0.0 1.0 1.0 1.0
BuildingElevator.BuildingElevator(String, int, BuildingController) 0.0 1.0 1.0 1.0
BuildingElevator.down() 1.0 1.0 2.0 2.0
BuildingElevator.getCur() 0.0 1.0 1.0 1.0
BuildingElevator.getElevatorId() 0.0 1.0 1.0 1.0
BuildingElevator.getPassengers() 0.0 1.0 1.0 1.0
BuildingElevator.isPassengerIn() 4.0 3.0 3.0 4.0
BuildingElevator.isPassengerOut() 3.0 3.0 2.0 3.0
BuildingElevator.isTake(PersonRequest) 6.0 4.0 2.0 7.0
BuildingElevator.look() 15.0 7.0 6.0 10.0
BuildingElevator.openClose() 2.0 1.0 3.0 3.0
BuildingElevator.passengersIn() 4.0 1.0 4.0 4.0
BuildingElevator.passengersOut() 3.0 1.0 3.0 3.0
BuildingElevator.response() 0.0 1.0 1.0 1.0
BuildingElevator.run() 2.0 1.0 3.0 3.0
BuildingElevator.running(int) 1.0 1.0 1.0 4.0
BuildingElevator.setAlive(boolean) 0.0 1.0 1.0 1.0
BuildingElevator.up() 1.0 1.0 2.0 2.0
FloorController.addElevator(int, int) 0.0 1.0 1.0 1.0
FloorController.FloorController(RequestQueue) 0.0 1.0 1.0 1.0
FloorController.run() 30.0 7.0 10.0 11.0
FloorController.setAlive(boolean) 0.0 1.0 1.0 1.0
FloorElevator.add(PersonRequest) 0.0 1.0 1.0 1.0
FloorElevator.FloorElevator(int, int) 0.0 1.0 1.0 1.0
FloorElevator.getFloor() 0.0 1.0 1.0 1.0
FloorElevator.getTodo() 0.0 1.0 1.0 1.0
FloorElevator.isPassengerIn() 4.0 3.0 3.0 4.0
FloorElevator.isPassengerOut() 3.0 3.0 2.0 3.0
FloorElevator.isTake(PersonRequest) 3.0 4.0 2.0 4.0
FloorElevator.left() 1.0 1.0 2.0 2.0
FloorElevator.look() 18.0 7.0 6.0 10.0
FloorElevator.openClose() 2.0 1.0 3.0 3.0
FloorElevator.passengersIn() 4.0 1.0 4.0 4.0
FloorElevator.passengersOut() 3.0 1.0 3.0 3.0
FloorElevator.response() 0.0 1.0 1.0 1.0
FloorElevator.right() 1.0 1.0 2.0 2.0
FloorElevator.run() 2.0 1.0 4.0 4.0
FloorElevator.running(int) 1.0 1.0 1.0 4.0
FloorElevator.setAlive(boolean) 0.0 1.0 1.0 1.0
FloorElevator.turnLeft(char, char) 0.0 1.0 1.0 1.0
Main.main(String[]) 0.0 1.0 1.0 1.0
RequestPool.RequestPool() 1.0 1.0 2.0 2.0
RequestPool.run() 21.0 3.0 9.0 10.0
RequestQueue.getBuildingController() 0.0 1.0 1.0 1.0
RequestQueue.getFloorController() 0.0 1.0 1.0 1.0
RequestQueue.getRequests() 0.0 1.0 1.0 1.0
RequestQueue.isEmpty() 0.0 1.0 1.0 1.0
RequestQueue.put(PersonRequest) 0.0 1.0 1.0 1.0
RequestQueue.RequestQueue() 0.0 1.0 1.0 1.0
RequestQueue.setBuildingController(BuildingController) 0.0 1.0 1.0 1.0
RequestQueue.setFloorController(FloorController) 0.0 1.0 1.0 1.0
RequestQueue.take() 0.0 1.0 1.0 1.0
SafeOutput.println(String) 0.0 1.0 1.0 1.0
Total 143.0 94.0 121.0 148.0
Average 2.4655172413793105 1.6206896551724137 2.086206896551724 2.5517241379310347

电梯类的look方法复杂度过高,原因是加入了很多对于乘客请求和当前电梯状态的判断,除此之外的方法复杂度都在10以下。

BUG分析

本次作业出现了轮询的BUG,原因是调度策略写的逻辑有问题。最初的调度策略是判断当前Controller请求列表中的请求是否能由电梯捎带,如果能就分发下去。但是不能捎带的请求就会被留在队列中,导致队列被反复查询造成CPU时长过高。当时写的时候重点考虑了如果都能捎带的情况下该如何分配,忽略了不能捎带的请求的问题,在本地正确性检验是没有问题的导致疏忽了轮询问题的检查。

第三次作业

要求:增加了对横向电梯的限制,限制横向电梯可到达的楼栋,同时同一层可以有多部横向电梯。本次作业乘客的请求可以同时跨楼层跨楼栋。

架构设计

架构和策略实现

本次作业修改了调度策略,采取自由竞争的做法,controller不再作为一个线程,而只是保存请求列表的容器。调度方法为,根据当前的电梯到达情况维护一张图,对每个请求都在这张图里找最短路,将请求拆分为只有横向或纵向的请求,由这些请求构成一个请求list发送给相应controller,由最早到达且能够处理该请求的电梯抢到请求并处理,对于没有一次处理完的请求,再将请求列表发回相应controller,直到该请求被完成。

UML图

image

UML协作图

image

线程安全控制

同步块设置上将Controller的请求list暴露给其管理的电梯。新增了Count类用以统计剩余未完成的请求数,该类由单例模式实现,暴露给InputThread和全部电梯。

线程安全退出上,利用Count计数,当input结束并且全部请求都被做完时,唤醒全部电梯结束其线程。

程序质量分析

度量分析

SafeOutput 1.0 1.0 1.0
RequestList 2.4285714285714284 7.0 34.0
Main 1.0 1.0 1.0
InputThread 4.5 7.0 9.0
FloorElevator 2.7857142857142856 8.0 39.0
FloorController 1.1666666666666667 2.0 7.0
ElevatorMap 3.4166666666666665 8.0 41.0
Count 1.8571428571428572 4.0 13.0
BuildingElevator 2.8461538461538463 8.0 37.0
BuildingController 1.0 1.0 6.0
Total 188.0
Average 2.473684210526316 4.7 18.8

weighted method complexity仍然时Elevator类较高,方法平均复杂度InputThread较高,原因同上。另一个复杂度较高的类时ElevatorMap, 该类主要负责建图和查找最短路,由于初始化嵌入了四层循环,所以导致复杂度较高。

方法圈复杂度分析

BuildingController.addElevator(int, double, int) 0.0 1.0 1.0 1.0
BuildingController.addRequest(RequestList) 0.0 1.0 1.0 1.0
BuildingController.BuildingController(String, int) 0.0 1.0 1.0 1.0
BuildingController.getRequestQueue() 0.0 1.0 1.0 1.0
BuildingController.isAlive() 0.0 1.0 1.0 1.0
BuildingController.setAlive(boolean) 0.0 1.0 1.0 1.0
BuildingElevator.BuildingElevator(String, int, double, int, BuildingController) 0.0 1.0 1.0 1.0
BuildingElevator.down() 1.0 1.0 2.0 2.0
BuildingElevator.isPassengerIn() 4.0 3.0 3.0 4.0
BuildingElevator.isPassengerOut() 3.0 3.0 2.0 3.0
BuildingElevator.isTake(PersonRequest) 6.0 4.0 2.0 7.0
BuildingElevator.look() 15.0 7.0 6.0 10.0
BuildingElevator.openClose() 2.0 1.0 3.0 3.0
BuildingElevator.passengersIn() 4.0 1.0 4.0 4.0
BuildingElevator.passengersOut() 6.0 1.0 4.0 4.0
BuildingElevator.response() 0.0 1.0 1.0 1.0
BuildingElevator.run() 2.0 1.0 4.0 4.0
BuildingElevator.running(int) 1.0 1.0 1.0 4.0
BuildingElevator.up() 1.0 1.0 2.0 2.0
Count.addRequest() 0.0 1.0 1.0 1.0
Count.config(ArrayList, FloorController) 0.0 1.0 1.0 1.0
Count.Count() 0.0 1.0 1.0 1.0
Count.finishRequest() 6.0 1.0 4.0 5.0
Count.getInstance() 0.0 1.0 1.0 1.0
Count.isAlive() 0.0 1.0 1.0 1.0
Count.setAlive(boolean) 6.0 1.0 4.0 5.0
ElevatorMap.addElevatorMap(int, int, int, int) 0.0 1.0 1.0 1.0
ElevatorMap.dijkstra(int[][], int, int) 10.0 3.0 4.0 8.0
ElevatorMap.ElevatorMap() 1.0 1.0 1.0 2.0
ElevatorMap.floorLength(int, int) 2.0 2.0 2.0 2.0
ElevatorMap.getElevatorMap() 0.0 1.0 1.0 1.0
ElevatorMap.getGraph() 0.0 1.0 1.0 1.0
ElevatorMap.getInstance() 0.0 1.0 1.0 1.0
ElevatorMap.getNearestNode(int[][], boolean[], int[]) 4.0 1.0 1.0 5.0
ElevatorMap.initGraph() 19.0 1.0 8.0 9.0
ElevatorMap.update(int[][], int, boolean[], int[], int[]) 8.0 1.0 1.0 6.0
ElevatorMap.updateBuildingGraph(int, int) 11.0 1.0 6.0 6.0
ElevatorMap.updateGraph(int, int, int) 12.0 1.0 6.0 8.0
FloorController.addElevator(int, int, double, int, int) 0.0 1.0 1.0 1.0
FloorController.addRequest(RequestList) 0.0 1.0 1.0 1.0
FloorController.FloorController() 1.0 1.0 1.0 2.0
FloorController.getRequestListQueue(int) 0.0 1.0 1.0 1.0
FloorController.isAlive() 0.0 1.0 1.0 1.0
FloorController.setAlive(boolean) 0.0 1.0 1.0 1.0
FloorElevator.FloorElevator(int, int, double, int, int, FloorController) 0.0 1.0 1.0 1.0
FloorElevator.isPassengerIn() 4.0 3.0 3.0 4.0
FloorElevator.isPassengerOut() 4.0 3.0 3.0 4.0
FloorElevator.isTake(PersonRequest) 8.0 5.0 6.0 8.0
FloorElevator.left() 1.0 1.0 2.0 2.0
FloorElevator.look() 22.0 7.0 11.0 15.0
FloorElevator.openClose() 2.0 1.0 3.0 3.0
FloorElevator.passengersIn() 4.0 1.0 4.0 4.0
FloorElevator.passengersOut() 7.0 1.0 5.0 5.0
FloorElevator.response() 0.0 1.0 1.0 1.0
FloorElevator.right() 1.0 1.0 2.0 2.0
FloorElevator.run() 2.0 1.0 4.0 4.0
FloorElevator.running(int) 1.0 1.0 1.0 4.0
FloorElevator.turnLeft(char, char) 0.0 1.0 1.0 1.0
InputThread.InputThread() 1.0 1.0 2.0 2.0
InputThread.run() 13.0 3.0 7.0 8.0
Main.main(String[]) 0.0 1.0 1.0 1.0
RequestList.dispatch() 2.0 1.0 3.0 3.0
RequestList.horizontal2vertical2horizontal(HashMap, PersonRequest) 12.0 3.0 6.0 8.0
RequestList.isDone() 1.0 2.0 2.0 2.0
RequestList.lossA(int, PersonRequest) 5.0 3.0 4.0 5.0
RequestList.lossB(int, PersonRequest) 0.0 1.0 1.0 1.0
RequestList.parser(PersonRequest) 1.0 2.0 2.0 2.0
RequestList.parserGraph(PersonRequest) 1.0 1.0 2.0 2.0
RequestList.peek() 0.0 1.0 1.0 1.0
RequestList.planA(int, PersonRequest) 2.0 1.0 3.0 3.0
RequestList.planB(int, PersonRequest) 0.0 1.0 1.0 1.0
RequestList.pop() 0.0 1.0 1.0 1.0
RequestList.RequestList(PersonRequest, ArrayList, FloorController) 0.0 1.0 1.0 1.0
RequestList.size() 0.0 1.0 1.0 1.0
RequestList.verical2horizontal2vertical(HashMap, PersonRequest) 11.0 3.0 6.0 7.0
SafeOutput.println(String) 0.0 1.0 1.0 1.0
Total 230.0 116.0 185.0 235.0
Average 3.026315789473684 1.5263157894736843 2.4342105263157894 3.0921052631578947

方法复杂度控制得y还算理想,除了FloorElevator的复杂度偏高其余都较均衡。

BUG分析

没有看到指导书里可以允许同楼层存在多部横向电梯,导致强测所有有多部横向电梯的点全部超时。原因时存储横向电梯采用HashMap存储,key为楼层,当新的电梯加入时会把原来电梯覆盖而导致原来电梯无法结束线程。解决方法是用ArrayList存储横向电梯,同时在策略上让横向电梯也采取自由竞争的方式即可。

hack策略

由于没有特别在意hack, 所以没有具体对同房间的代码进行分析。测试的思路比较统一,就是在尽可能把电梯安排满的情况下,在一个较靠后的时间统一发放大量请求。本单元的作业测试比较困难,不同于第一单元重点对于代码功能逻辑的测试,本单元的测试重点更多在于多个线程间是否是正常协作和唤醒的。

性能策略

本着越接近现实生活的方式越好的想法,在第一二次作业的调度策略中都尽可能让最近的电梯去接人,并且尽可能让电梯都带满人。电梯本身运行采用look策略性能还算优秀,就不需要对于单独电梯的运行写图论算法了。而实践表明,调度策略大概率是比不过自由竞争的,细致的调度比较困难且调度本身需要消耗时间,所以第三次就采用了自由竞争的策略。对于第三次作业而言,一定会涉及到换乘的情况。而单纯的去找能够从出发楼座到目的楼座的横向电梯在请求多的时候很难找到最优解,于是采用dijkstra算法。其优点在于可以降低送达过程中电梯的运行时间,但缺点在于可能会存在多次的开关门。但是由于问题规模不大,多次开关门的情况出现的概率较小,实际性能也比较客观。

心得体会

就层次化设计而言,本单元作业的层次其实没有第一单元复杂,没有多个从上向下的层次关系,各个模块如input,elevator,controller也比较清晰,功能复杂性上也没有上个单元复杂。但这个单元的层次化设计,侧重于对于临界区的设计,需要将哪些对象共享,需要让哪些对象暴露给哪些对象,是本次单元重点考察的。

就线程安全而言,掌握了生产者消费者模式,对于java的多线程编程模型有了了解。在线程安全中,需要重点考虑wait-notify的关系,和资源访问的顺序。对于诸如输出等操作,要考虑这类操作是否需要变成原子型操作。

posted @ 2022-05-02 15:02  刘鸿睿SC  阅读(27)  评论(1编辑  收藏  举报