BUAA_OO_2022_Unit_2_Summary
作者:互联网
零、目录
- 程序设计构架
- 第一次作业
- 第二次作业
- 第三次作业
- 协作图
- 调度器设计
- 同步块与锁分析
- 电梯逻辑分析
- 程序BUG分析
- 自测/互测策略
- 心得体会
一、程序设计构架
第一次作业
-
需求简述:
模拟一个多线程实时电梯系统,各楼座有且仅有一台纵向电梯,处理已知起终点的同楼座乘客请求。
-
代码构架:
|- Unit2:主类 |- InputHandler:输入线程 |- Controller:各楼座候乘表 |- Elevator:电梯线程 |- OutputHandler:输出处理
采用生产者-消费者设计模式,托盘为各楼座候乘表,即实际上有5张候乘表。
同一楼座的电梯共享该楼座的候乘表,读写信息均在候乘表中加锁实现,避免几个线程之间直接交互以及数据不同步的问题。
对 TimableOutput 加锁封装为输出处理类,避免出现以下情况导致输出时间戳非递增:
1获取时间戳 --> 2获取时间戳 --> 2输出 --> 1输出
-
规模分析:
共221个代码行
-
复杂度分析:
method ev(G) iv(G) v(G) Controller.addRequest(PersonRequest) 1.0 1.0 1.0 Controller.getRequests() 1.0 1.0 1.0 Controller.isInputAlive() 1.0 1.0 1.0 Controller.setInputAlive(boolean) 1.0 1.0 1.0 Controller.setRequests(ArrayList) 1.0 1.0 1.0 Elevator.Elevator(char, int, Controller) 1.0 1.0 1.0 Elevator.move(long) 1.0 1.0 2.0 Elevator.openAndClose() 1.0 13.0 15.0 InputHandler.InputHandler(ElevatorInput, HashMap) 1.0 1.0 1.0 OutputHandler.initStartTimestamp() 1.0 1.0 1.0 OutputHandler.println(String) 1.0 1.0 1.0 Unit2.main(String[]) 1.0 3.0 3.0 InputHandler.run() 3.0 6.0 6.0 Elevator.run() 9.0 22.0 25.0 Total 24.0 54.0 60.0 Average 1.71 3.86 4.29
第二次作业
-
需求简述:
模拟一个多线程实时电梯系统,初始各楼座有且仅有一台纵向电梯;可动态增加横向、纵向电梯;处理已知起终点的同楼座或同楼层乘客请求。
-
代码构架:
|- Unit2:主类 |- InputHandler:输入线程 |- GlobalController:总候乘表 |- Controller:各楼座/层候乘表 |- Elevator:电梯线程 |- ElevatorP:横向电梯线程 |- ElevatorV:纵向电梯线程 |- OutputHandler:输出处理
仅为完成本次作业的代码与第一次作业基本相同,只是将电梯线程抽象出来——把方向计算、距离计算抽象成子函数,并在横向、纵向电梯类内具体实现这些操作。此时,两类电梯的开关门、运行、捎带等策略完全一致,可以将第一次作业的代码完全复用,并且没有出现大段重复代码。
因上述实现较为简单,我在本次作业中尝试提前实现换乘。将原来的一级托盘结构更改为二级托盘结构,分别装载所有请求和具体座/层请求。详见后文“调度器设计”。
-
规模分析:
共489个代码行
-
复杂度分析:
method ev(G) iv(G) v(G) Controller.addRequest(PersonRequest) 1.0 1.0 1.0 Controller.getRequests() 1.0 1.0 1.0 Controller.isInputAlive() 1.0 1.0 1.0 Controller.setInputAlive(boolean) 1.0 1.0 1.0 Controller.setRequests(ArrayList) 1.0 1.0 1.0 Elevator.Elevator(int, GlobalController, Controller) 1.0 1.0 1.0 Elevator.getBlock() 1.0 1.0 1.0 Elevator.getDirection() 1.0 1.0 1.0 Elevator.getEleId() 1.0 1.0 1.0 Elevator.getFloor() 1.0 1.0 1.0 Elevator.inDir(PersonRequest) 1.0 1.0 1.0 Elevator.openAndClose() 1.0 5.0 5.0 Elevator.setBlock(char) 1.0 1.0 1.0 Elevator.setCtrlKey(String) 1.0 1.0 1.0 Elevator.setDirection(int) 1.0 1.0 1.0 Elevator.setFloor(int) 1.0 1.0 1.0 Elevator.takeIn(String) 1.0 11.0 11.0 Elevator.takeOff(String) 1.0 4.0 4.0 Elevator.waitInDir(PersonRequest) 1.0 1.0 1.0 ElevatorP.ElevatorP(int, int, GlobalController, Controller) 1.0 1.0 1.0 ElevatorP.isFrom(PersonRequest) 1.0 1.0 1.0 ElevatorP.isTo(PersonRequest) 1.0 1.0 1.0 ElevatorP.move(long) 1.0 4.0 4.0 ElevatorP.reqDir(PersonRequest) 1.0 1.0 3.0 ElevatorP.waitDir(PersonRequest) 1.0 1.0 3.0 ElevatorV.ElevatorV(char, int, GlobalController, Controller) 1.0 1.0 1.0 ElevatorV.isFrom(PersonRequest) 1.0 1.0 1.0 ElevatorV.isTo(PersonRequest) 1.0 1.0 1.0 ElevatorV.reqDir(PersonRequest) 1.0 1.0 1.0 ElevatorV.waitDir(PersonRequest) 1.0 1.0 1.0 GlobalController.GlobalController() 1.0 1.0 1.0 GlobalController.addController(ElevatorRequest) 1.0 2.0 2.0 GlobalController.addElevator(ElevatorRequest) 1.0 3.0 3.0 GlobalController.addElevatorP(ElevatorRequest) 1.0 2.0 2.0 GlobalController.addElevatorV(ElevatorRequest) 1.0 2.0 2.0 GlobalController.infoInputKilled() 1.0 2.0 2.0 GlobalController.initControllers() 1.0 2.0 2.0 GlobalController.initElevators() 1.0 2.0 2.0 GlobalController.withoutTransfers() 1.0 1.0 1.0 InputHandler.InputHandler(ElevatorInput) 1.0 1.0 1.0 OutputHandler.initStartTimestamp() 1.0 1.0 1.0 OutputHandler.println(String) 1.0 1.0 1.0 Unit2.main(String[]) 1.0 1.0 1.0 GlobalController.addNextRequest(String, PersonRequest) 3.0 4.0 5.0 InputHandler.run() 3.0 6.0 6.0 ElevatorV.move(long) 4.0 4.0 4.0 Elevator.ifOpen() 5.0 11.0 12.0 Elevator.run() 5.0 12.0 14.0 GlobalController.addRequest(PersonRequest) 7.0 8.0 10.0 Total 70.0 116.0 126.0 Average 1.43 2.37 2.57
第三次作业
-
需求简述:
模拟一个多线程实时电梯系统,初始各楼座有且仅有一台纵向电梯、1层有且仅有一台可达所有楼座的横向电梯;可动态增加横向、纵向电梯,新电梯可自定义限载人数、运行速度、(横向电梯)可达楼座;处理已知起终点的任意乘客请求。
-
代码构架
|- Unit2:主类 |- InputHandler:输入线程 |- GlobalController:总候乘表 |- Controller:各楼座/层候乘表 |- Elevator:电梯线程 |- ElevatorP:横向电梯线程 |- ElevatorV:纵向电梯线程 |- OutputHandler:输出处理
-
规模分析:
共557个代码行
-
复杂度分析:
method ev(G) iv(G) v(G) Controller.addElevator(ElevatorRequest) 1.0 1.0 1.0 Controller.addRequest(PersonRequest) 1.0 1.0 1.0 Controller.getElevators() 1.0 1.0 1.0 Controller.getRequests() 1.0 1.0 1.0 Controller.isInputAlive() 1.0 1.0 1.0 Controller.setInputAlive(boolean) 1.0 1.0 1.0 Controller.setRequests(ArrayList) 1.0 1.0 1.0 Elevator.Elevator(int, int, long, GlobalController, Controller) 1.0 1.0 1.0 Elevator.getBlock() 1.0 1.0 1.0 Elevator.getCtrl() 1.0 1.0 1.0 Elevator.getDirection() 1.0 1.0 1.0 Elevator.getEleId() 1.0 1.0 1.0 Elevator.getFloor() 1.0 1.0 1.0 Elevator.inDir(PersonRequest) 1.0 1.0 1.0 Elevator.move(long) 1.0 1.0 1.0 Elevator.openAndClose() 1.0 5.0 5.0 Elevator.setBlock(char) 1.0 1.0 1.0 Elevator.setCtrlKey(String) 1.0 1.0 1.0 Elevator.setDirection(int) 1.0 1.0 1.0 Elevator.setFloor(int) 1.0 1.0 1.0 Elevator.specialWait() 1.0 1.0 1.0 Elevator.takeIn(String) 1.0 13.0 13.0 Elevator.takeOff(String) 1.0 4.0 4.0 Elevator.waitInDir(PersonRequest) 1.0 1.0 1.0 ElevatorP.ElevatorP(int, int, int, long, int, GlobalController, Controller) 1.0 1.0 1.0 ElevatorP.canCarry(PersonRequest) 1.0 1.0 1.0 ElevatorP.canStop() 1.0 1.0 1.0 ElevatorP.isFrom(PersonRequest) 1.0 1.0 1.0 ElevatorP.isTo(PersonRequest) 1.0 1.0 1.0 ElevatorP.move(long) 1.0 4.0 4.0 ElevatorP.reqDir(PersonRequest) 1.0 1.0 3.0 ElevatorP.waitDir(PersonRequest) 1.0 1.0 3.0 ElevatorV.ElevatorV(char, int, int, long, GlobalController, Controller) 1.0 1.0 1.0 ElevatorV.canCarry(PersonRequest) 1.0 1.0 1.0 ElevatorV.canStop() 1.0 1.0 1.0 ElevatorV.isFrom(PersonRequest) 1.0 1.0 1.0 ElevatorV.isTo(PersonRequest) 1.0 1.0 1.0 ElevatorV.reqDir(PersonRequest) 1.0 1.0 1.0 ElevatorV.waitDir(PersonRequest) 1.0 1.0 1.0 GlobalController.GlobalController() 1.0 1.0 1.0 GlobalController.addController(ElevatorRequest) 1.0 2.0 2.0 GlobalController.addElevator(ElevatorRequest) 1.0 3.0 3.0 GlobalController.addElevatorP(ElevatorRequest) 1.0 2.0 2.0 GlobalController.addElevatorV(ElevatorRequest) 1.0 2.0 2.0 GlobalController.addRequest(PersonRequest) 1.0 5.0 5.0 GlobalController.infoInputKilled() 1.0 2.0 2.0 GlobalController.initControllers() 1.0 2.0 2.0 GlobalController.initElevators() 1.0 2.0 2.0 GlobalController.withoutTransfers() 1.0 1.0 1.0 InputHandler.InputHandler(ElevatorInput) 1.0 1.0 1.0 OutputHandler.initStartTimestamp() 1.0 1.0 1.0 OutputHandler.println(String) 1.0 1.0 1.0 Unit2.main(String[]) 1.0 1.0 1.0 ElevatorP.specialWait() 3.0 2.0 3.0 GlobalController.addNextRequest(String, PersonRequest) 3.0 4.0 5.0 InputHandler.run() 3.0 6.0 6.0 ElevatorV.move(long) 4.0 4.0 4.0 Elevator.run() 5.0 13.0 15.0 GlobalController.findTranFloor(char, char, int, int) 5.0 3.0 6.0 Elevator.ifOpen() 6.0 12.0 14.0 Total 82.0 131.0 144.0 Average 1.37 2.18 2.40
协作图
附:度量分析条目解释
- OC:类的非抽象方法圈复杂度,继承类不计入
- WMC:类的总圈复杂度
- ev(G):非抽象方法的基本复杂度,用以衡量一个方法的控制流结构缺陷,范围是 [1, v(G)]
- iv(G):方法的设计复杂度,用以衡量方法控制流与其他方法之间的耦合程度,范围是 [1, v(G)]
- v(G):非抽象方法的圈复杂度,用以衡量每个方法中不同执行路径的数量
二、调度器设计
三、同步块与锁分析
本单元代码中的所有用到共享对象的部分,我都选择了使用 synchronized 加锁。主要用途如下:
-
共享对象的类方法
如果方法涉及到读写本类内属性的操作,给该方法上锁。
// Controller.java
// requests为类内属性
public synchronized void addRequest(PersonRequest pr) {
requests.add(pr);
this.notifyAll();
}
-
线程对象中的同步块
如果线程对象中涉及到连续读写共享对象,且上下文连接紧密,不可中断的操作,给该代码块上锁。
设置同步块的大多数场景为:电梯对象遍历访问候乘表的所有请求。此时须保证遍历的全过程中候乘表不能被更改,否则将会出现线程不安全错误。
// ElevatorP.java
// getCtrl()返回共享对象
synchronized (getCtrl()) {
for (PersonRequest x: getCtrl().getRequests()) {
char from = x.getFromBuilding();
char to = x.getToBuilding();
if (((switchInfo >> (from - 'A')) & 1) +
((switchInfo >> (to - 'A')) & 1) == 2) {
return false;
}
}
}
在第一次作业中,我的共享对象全部是同一个类的实例,且共享对象之间不会有互相访问的情况。因此,用最粗暴的方法“只要涉及共享对象,全部上锁!”不会导致死锁。
从第二次作业开始,我将原来的生产-消费模式中的一级托盘重构为二级托盘。因此在实现时特别留意,防止两类共享对象肆意加锁导致的死锁。具体策略如下:
- 可以锁住一级托盘访问二级托盘,二级托盘锁内不可访问一级托盘
// GlobalController.java
// 一级托盘:GlobalController,二级托盘:Controller
public synchronized void infoInputKilled() {
this.inputAlive = false;
for (Controller ctrl : controllers.values()) {
synchronized (ctrl) {
ctrl.setInputAlive(false);
ctrl.notifyAll();
}
}
}
- 线程对象中,二级托盘同步块内避免调用其他共享对象的(加锁)方法、避免二次设置同步块
// Elevator.java
// gctrl为一级托盘对象,ctrl为二级托盘对象
boolean transEnd = gctrl.withoutTransfers(); // 在二级托盘同步块外调用一级托盘
synchronized (ctrl) {
if ((ctrl.getRequests().isEmpty() || specialWait()) && direction == 0) {
if (ctrl.isInputAlive() || !transEnd) { // *
...
} else { return; }
} else {
...
}
}
由于我的同步块内基本上都是读写并举,改用读写锁的轻量化意义不大,所以全程只用了 synchronized 的方式上锁。
标签:OO,2.0,1.0,BUAA,Elevator,Controller,GlobalController,PersonRequest,Unit 来源: https://www.cnblogs.com/ChorlingLau/p/16197054.html