2020 OO 第二单元总结

只要跑得够快即使从头关到尾你也喜欢吗?


 

一、设计策略

1.1 总体策略概述

在多线程的协同和同步控制方面,我三次作业都是采用生产者/消费者模式(还憨憨地在内部分了customer、producer、tray的包……方便自己看orz)。

其中“生产者”为输入线程,将读取到的Request放到“货架”Scheduler上;“消费者”则是每个电梯线程,以一种类似观察者模式的方式追踪“货架”Scheduler的变化。之所以说是类似,是因为我每次都是在电梯自身状态发生改变后获取当前的“货架”Scheduler内容,而不是Scheduler一有更新就通知Elevator(也可能这是某种设计模式但我不知道)。

这样设计的好处是,我只有Scheduler类的方法是上锁的,其他类调用时不需要考虑是否会引发线程不安全问题,写起来很方便;坏处是我现在还不是很熟悉lock的用法……

电梯内部则采用状态模式,设置了WAIT, OPEN, CLOSE, CLOSED, PASS, UP, DOWN多个枚举状态类型。

而对于对于存储人群信息的队列,我采用了单独的WaitMul、ElevatorMul封装HashMap<Integer, Queue<Person>>的方式,指向来去楼层,并配置各种方法,外部可以对Multitude进行塞人取人的操作,Multitude内部的存储方式是不透明的。

 

1.2 性能优化策略

1.2.1 第五次作业

性能上,本次作业我采用的是在SSTF的基础上,自己瞎琢磨的“选择最短路径”贪心算法,比较电梯原目标与反方向新目标的最短距离:

 private static int minDistance(int cntFloor, int goalFloor, int tmpFloor, int tmpGoal) {
  int dis1 = Math.abs(goalFloor - cntFloor) + Math.abs(goalFloor - tmpFloor)
         + Math.abs(tmpGoal - tmpFloor);
  int dis2 = Math.abs(cntFloor - tmpFloor) + Math.abs(tmpGoal - tmpFloor)
         + Math.abs(goalFloor - tmpGoal);
     if (dis1 > dis2) {
    return tmpGoal;
    } else {
    return goalFloor;
    }
 }

然而这种方法有很大的缺陷……在某些特定楼层会有小几率出现反复横跳……最后逼近截止时间一直在改,又增加了防止锁死的机制(当检测到死循环时弃用这一优化),代价是拉低了性能,最后性能分16多。

 

1.2.2 第六次作业

感谢CJB,他在第五次作业得到很高的性能分,也慷概地将他的算法分享给了我们——

sstf的基础上,当电梯里有人时候,选择最近上楼或最近出楼层的,就行,其他不变。

——《CJB语录》第二卷上册

所以在第六次多电梯,我的单电梯也采用了相同(也许)的策略。

 /***************
  * SSTF
  * *************/
 private int getGoalFloor(int cnt, boolean isEleFull) {
     // 传入参数:电梯当前楼层、电梯满员状态
     if (elevator.isEmpty()) {
         return scheduler.getMinDisWhenEmpty(cnt, eleId);
         // 电梯为空时,按情况进入wait状态
    } else {
         int inReq = elevator.getMinDisReq(cnt, goalFloor);
         if (isEleFull) {
             return inReq;
             // 电梯满员时,不考虑外部新请求
        }
         int outReq = scheduler.getMinDis(cnt, inReq, eleId);
         if (outReq == LOST) {
             return inReq;
             // 外部没有请求,直接相应内部请求
        } else {
             return Math.abs(outReq - cnt) < Math.abs(inReq - cnt) ? outReq : inReq;
             // 选择最近上楼或最近出电梯的
        }
    }
 }

射射CJB!CJB, YYDS! 最后性能分19多。

 

1.2.3 第七次作业

在第七次作业沿用了CJB的SSTF策略以及部分,为啥不用LOOK呢?我感觉这种性能分权重还是优先接人收益高。

而对于换乘,我用到了两组数组:

 private static final int[][] canStopOfType = {
     /*********1***********************9****************15****************/
    {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
    {0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
    {0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0}
     /*********1***********************9****************15****************/
 };
 private static final int[][] mustFromName = {
     /********(1)**********************9***************(15)***************/
    {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1},
    {0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}
     /*********1***********************9****************15****************/
 };
 /*** A B C ***/

canStopOfType存储的是电梯可以停的楼层,mustFromName数组则是电梯必须接受这一楼层的请求(1和15层三个类型都可以,就让电梯自由竞争)。

换乘时会把人从电梯重新塞进等待队列,所以之后就与没有换乘的情况一样了。其实感觉5层作为换乘站也不错……后来不想改了……最后性能分19多。

 

二、可扩展性

(2)从功能设计和性能设计的平衡方面,更细和总结自己第三次作业架构设计的可扩展性

可扩展性方面,这一次一开始就希望能为后续的扩展留好迭代空间,在正式开始之前读了好多学长的博客,其中PMXM助教的博客给了我很大启发:

https://www.cnblogs.com/i-love-ange-and-oo-forever/p/10756828.html

最后迭代开发的效果还算马马虎虎……但每次加入新功能都会引入不少Bug,测试花的时间很多。

 

功能设计

电梯是一台有限状态机,有WAIT, OPEN, CLOSE, CLOSED, PASS, UP, DOWN一共7种状态,三次作业的状态转移图都差不多,如下:

这样做的优势是每部电梯自身都独挡一面,人可以随时选择最近的电梯、呼叫多部电梯,方便增加电梯数量的扩展。

 

性能设计

根据上面那个丑哭的状态转移图可知,在CLOSED, PASS两种状态下会调用获取目标楼层的方法,因此我所有的优化都只要写在获取目标楼层的方法getGoalFloor中,同样有利于迭代开发,比如第七次作业的getGoalFloor方法和前面提到过的第六次作业getGoalFloor方法比较,完全没有变化。

 

SOLID原则分析

以第七次作业分析:

  • SRP原则:感觉还行,RequestParser将读取到的PersonRequest放到Scheduler上,ElevatorRequest则传入Controller用于创造新电梯;ElevatorController运送Scheduler上的乘客。

  • OCP原则:电梯封装的好,便于扩展数量,感觉这方面还行。

  • LSP原则:在第五次作业,对于WaitMul、ElevatorMul采用了继承的方式,但后来在第六、七次作业的优化中取消……所以应该没有怎么实现。

  • LOD原则:RequestParser与ElevatorController通过Scheduler转发调用,有独立性。

  • ISP原则:在本单元作业没有创建接口,部分线程实现了Runnable接口。三种电梯我是采用构造时传参的方式,配置工厂类Controller用于创造新电梯,所以也就没有实现接口的必要了。

  • DIP原则:感觉做的还可以……因为我都是人自己刷新要乘坐的电梯(但是因为没有给人开线程,所以都是由电梯调用人的选电梯方法,其实实现上来说更类似于电梯抢人),避免了为了优化带来的倒置依赖。

 

三、度量分析

3.1 第五次作业

▼ 基本结构图,MainClass启动输入线程和电梯线程,外部的楼层等待人群和内部的电梯货运人群采用了继承抽象类人群的方式,将对人的存储方式封装起来。

▼ 时序图,用插件自动生成的,感觉不是很准确……感觉参看我的有限状态机图更好一点……

▼ 依赖关系,还可以。

ClassCyclicDcyDcy*DptDpt*
homework.MainClass 0 3 10 0 0
homework.custumer.Elevator 0 4 5 1 2
homework.custumer.Elevator.Status 0 0 0 2 3
homework.custumer.ElevatorController 0 3 8 1 1
homework.producer.RequestParser 0 2 5 1 1
homework.tray.MulInside 0 3 3 1 3
homework.tray.MulOutside 0 3 3 1 4
homework.tray.Multitude 0 2 2 4 7
homework.tray.Multitude.Type 0 0 0 3 8
homework.tray.Person 0 0 0 6 8
homework.tray.Scheduler 0 3 4 3 3

▼ 复杂度,ElevatorController作为一台大状态机以及“抢人”的独立个体,自身的run方法作为状态机判断条件很多,圈复杂度也飘红;获取最优目标的getGoal方法这次写的很复杂……

Methodev(G)iv(G)v(G)
homework.MainClass.main(String[]) 1 1 1
homework.custumer.Elevator.Elevator() 1 1 1
homework.custumer.Elevator.close(HashMap<Person, Integer>) 1 1 1
homework.custumer.Elevator.getCurrentFloor() 1 1 1
homework.custumer.Elevator.getMinTimeReq(int,int) 1 1 1
homework.custumer.Elevator.getStatus() 1 1 1
homework.custumer.Elevator.isEmpty() 1 1 1
homework.custumer.Elevator.move(boolean) 1 4 4
homework.custumer.Elevator.open() 1 1 1
homework.custumer.Elevator.setStatus(Status) 1 1 1
homework.custumer.Elevator.sleepTime(int) 1 1 2
homework.custumer.ElevatorController.ElevatorController(Scheduler) 1 1 1
homework.custumer.ElevatorController.getGoal1(int) 5 3 5
homework.custumer.ElevatorController.getGoal2(int) 6 3 7
homework.custumer.ElevatorController.getGoalFloor(int) 1 1 1
homework.custumer.ElevatorController.getGoalFloor2(int) 2 2 2
homework.custumer.ElevatorController.moveJudge(int) 2 2 6
homework.custumer.ElevatorController.run() 2 8 15
homework.custumer.ElevatorController.setDie2(boolean) 2 1 10
homework.producer.RequestParser.RequestParser(Scheduler) 1 1 1
homework.producer.RequestParser.run() 3 3 4
homework.tray.MulInside.MulInside() 1 1 1
homework.tray.MulInside.addInsideMul(HashMap<Person, Integer>) 1 2 2
homework.tray.MulInside.makeMultitudeOut(int) 1 2 2
homework.tray.MulOutside.MulOutside() 1 1 1
homework.tray.MulOutside.addOutsideMul(Person) 1 1 1
homework.tray.Multitude.Multitude(Type) 1 2 2
homework.tray.Multitude.cloneMulInFloor(int) 1 2 2
homework.tray.Multitude.convertFloor(int) 1 1 1
homework.tray.Multitude.getList() 1 1 1
homework.tray.Multitude.getMaxFloor() 1 1 1
homework.tray.Multitude.getMinDisFloor(int,int,int) 14 3 17
homework.tray.Multitude.getMinFloor() 1 1 1
homework.tray.Multitude.getMulInFloor(int) 1 1 1
homework.tray.Multitude.isEmpty() 3 2 3
homework.tray.Multitude.isEmptyFloor(int) 1 1 1
homework.tray.Multitude.removeMulInFloor(int) 1 1 1
homework.tray.Person.Person(PersonRequest) 1 1 1
homework.tray.Person.Person(int,int,int) 1 1 1
homework.tray.Person.clone() 1 1 1
homework.tray.Person.equals(Object) 2 1 2
homework.tray.Person.getFr() 1 1 1
homework.tray.Person.getTo() 1 1 1
homework.tray.Person.hashCode() 1 1 1
homework.tray.Person.makePersonIn() 1 1 1
homework.tray.Person.makePersonOut() 1 1 1
homework.tray.Person.moveUp() 1 1 1
homework.tray.Scheduler.Scheduler() 1 1 1
homework.tray.Scheduler.getMinDisFloor(int,int) 1 4 4
homework.tray.Scheduler.getOtherReq(int,int) 7 11 13
homework.tray.Scheduler.hasPassReqInFloor(int,int) 4 3 4
homework.tray.Scheduler.hasRequestInFloor(int) 1 1 1
homework.tray.Scheduler.makeMulIn(int) 1 2 2
homework.tray.Scheduler.minDistance(int,int,int,int) 2 1 2
homework.tray.Scheduler.readNewRequest(Person) 1 1 1
homework.tray.Scheduler.stopReading() 1 1 1
homework.tray.Scheduler.stopTask() 1 1 1

类复杂度上,ElevatorController飘红。

homework.MainClass11
homework.custumer.Elevator 1.3 13
homework.custumer.Elevator.Status n/a 0
homework.custumer.ElevatorController 5.12 41
homework.producer.RequestParser 2 4
homework.tray.MulInside 1.67 5
homework.tray.MulOutside 1 2
homework.tray.Multitude 2.55 28
homework.tray.Multitude.Type n/a 0
homework.tray.Person 1.1 11
homework.tray.Scheduler 2.6 26

 

3.2 第六次作业

▼ 基本结构图,MainClass启动输入线程和多个电梯线程,这次放弃了继承,并且优化了传递人员的方式,降低了耦合性。

▼ 时序图。

▼ 依赖关系,这次的架构是在第五次的基础上修改,也差不多。

ClassCyclicDcyDcy*DptDpt*
homework.MainClass 0 3 9 0 0
homework.custumer.Controller 0 2 7 1 1
homework.custumer.Elevator 0 3 3 1 3
homework.custumer.Elevator.Status 0 0 0 2 4
homework.custumer.ElevatorController 0 3 6 1 2
homework.custumer.ElevatorMul 0 1 1 1 4
homework.producer.RequestParser 0 2 3 1 1
homework.tray.Person 0 0 0 5 8
homework.tray.Scheduler 0 2 2 4 4
homework.tray.WaitMul 0 1 1 1 5

▼ 复杂度,几个寻找最优解的方法都较为复杂,比如getBestOne中用了大量循环遍历比较获取最小值,基本复杂度和圈复杂度都比较差。

Methodev(G)iv(G)v(G)
homework.MainClass.main(String[]) 1 1 1
homework.custumer.Controller.Controller(int,Scheduler) 1 1 1
homework.custumer.Controller.run() 1 2 2
homework.custumer.Elevator.Elevator(char) 1 1 1
homework.custumer.Elevator.close() 1 1 1
homework.custumer.Elevator.getCurrentFloor() 1 1 1
homework.custumer.Elevator.getMinDisReq(int,int) 1 1 1
homework.custumer.Elevator.getStatus() 1 1 1
homework.custumer.Elevator.isEmpty() 1 1 1
homework.custumer.Elevator.isFull() 1 1 1
homework.custumer.Elevator.move(boolean) 1 1 1
homework.custumer.Elevator.open() 1 1 1
homework.custumer.Elevator.putOne(Person) 1 1 1
homework.custumer.Elevator.setStatus(Status) 1 1 1
homework.custumer.Elevator.sleepTime(int) 1 1 2
homework.custumer.Elevator.updateCurrentFloor(boolean) 1 1 4
homework.custumer.ElevatorController.ElevatorController(Scheduler,char) 1 1 1
homework.custumer.ElevatorController.eleClose(int) 1 3 3
homework.custumer.ElevatorController.eleClosed(int,boolean) 1 5 6
homework.custumer.ElevatorController.eleWait(int,boolean) 2 4 4
homework.custumer.ElevatorController.getGoalFloor(int,boolean) 4 3 5
homework.custumer.ElevatorController.run() 2 4 11
homework.custumer.ElevatorMul.ElevatorMul() 1 2 2
homework.custumer.ElevatorMul.convertInFloor(int) 3 1 3
homework.custumer.ElevatorMul.convertOutFloor(int) 2 1 2
homework.custumer.ElevatorMul.getMinDisReq(int,int) 8 3 12
homework.custumer.ElevatorMul.isEleEmpty() 2 1 2
homework.custumer.ElevatorMul.isEleFull() 2 1 2
homework.custumer.ElevatorMul.mulOut(int) 1 2 2
homework.custumer.ElevatorMul.putOne(Person) 1 1 1
homework.custumer.ElevatorMul.updateEleSize(boolean) 1 1 2
homework.producer.RequestParser.RequestParser(Scheduler,ElevatorInput) 1 1 1
homework.producer.RequestParser.run() 3 3 4
homework.tray.Person.Person(PersonRequest) 1 1 1
homework.tray.Person.eleIdNull() 1 1 1
homework.tray.Person.equals(Object) 2 1 2
homework.tray.Person.getEl() 1 1 1
homework.tray.Person.getFr() 1 1 1
homework.tray.Person.getId() 1 1 1
homework.tray.Person.getMoveDis() 1 1 1
homework.tray.Person.getTo() 1 1 1
homework.tray.Person.getWaitDis() 1 1 1
homework.tray.Person.hashCode() 1 1 1
homework.tray.Person.makePersonIn() 1 1 1
homework.tray.Person.makePersonOut() 1 1 1
homework.tray.Person.moveUp() 1 1 1
homework.tray.Person.sameForward(boolean) 1 1 1
homework.tray.Person.setEl(char) 1 1 1
homework.tray.Person.setWaitDis(int) 1 1 1
homework.tray.Person.toString() 1 1 1
homework.tray.Scheduler.Scheduler() 1 1 1
homework.tray.Scheduler.failToGetOne(int,char) 1 1 1
homework.tray.Scheduler.getMinDis(int,int,char) 1 1 1
homework.tray.Scheduler.getMinDisWhenEmpty(int,char) 1 4 4
homework.tray.Scheduler.getOne(int,int,char) 1 1 1
homework.tray.Scheduler.hasRequestInFloor(int) 1 1 1
homework.tray.Scheduler.readNewRequest(Person) 1 1 1
homework.tray.Scheduler.stopReading() 1 1 1
homework.tray.Scheduler.stopTask() 1 1 1
homework.tray.Scheduler.updateFlag(int,char) 1 1 1
homework.tray.WaitMul.WaitMul() 1 2 2
homework.tray.WaitMul.addOutsideMul(Person) 1 1 1
homework.tray.WaitMul.convertInFloor(int) 3 1 3
homework.tray.WaitMul.convertOutFloor(int) 2 1 2
homework.tray.WaitMul.failToGetOne(int,char) 6 5 7
homework.tray.WaitMul.getBestOne(int,int,char) 1 1 1
homework.tray.WaitMul.getMinDisFloor(int,int,char) 8 3 12
homework.tray.WaitMul.hasRequestInFloor(int) 2 1 2
homework.tray.WaitMul.isEmpty() 3 2 3
homework.tray.WaitMul.searchBestOne(int,int,int,char) 4 7 9
homework.tray.WaitMul.updateFlag(int,char) 1 4 4

类复杂度上,这次的WaitMul设计由于放弃了继承关系,也不是很乐观。

ClassOCavgWMC
homework.MainClass 1 1
homework.custumer.Controller 1.5 3
homework.custumer.Elevator 1.23 16
homework.custumer.Elevator.Status n/a 0
homework.custumer.ElevatorController 4.17 25
homework.custumer.ElevatorMul 3 27
homework.producer.RequestParser 2 4
homework.tray.Person 1.06 18
homework.tray.Scheduler 1.1 11
homework.tray.WaitMul 3.91 43

 

3.3 第七次作业

▼ 基本结构图,MainClass启动输入线程和电梯线程。

▼ 时序图,这次加了SafeOutput的封装,虽然不加也没什么影响……

▼ 依赖关系,同第六次作业。

ClassCyclicDcyDcy*DptDpt*
homework.MainClass 6 2 12 3 6
homework.SafeOutput 0 0 0 2 9
homework.custumer.Controller 6 2 12 1 6
homework.custumer.Elevator 6 6 12 1 6
homework.custumer.Elevator.EleTime 0 0 0 2 7
homework.custumer.Elevator.Status 0 0 0 2 7
homework.custumer.ElevatorController 6 6 12 1 6
homework.custumer.ElevatorMul 0 1 2 1 7
homework.producer.RequestParser 6 3 12 1 6
homework.tray.Person 0 1 1 6 8
homework.tray.Scheduler 6 2 12 4 6
homework.tray.WaitMul 6 3 12 1 6

▼ 复杂度,问题和之前的都差不多。

Methodev(G)iv(G)v(G)
homework.MainClass.main(String[]) 1 1 1
homework.SafeOutput.println(String) 1 1 1
homework.custumer.Controller.Controller(Scheduler) 1 1 1
homework.custumer.Controller.addElevator(ElevatorRequest) 1 1 1
homework.custumer.Controller.convertType(String) 3 2 3
homework.custumer.Controller.run() 1 1 1
homework.custumer.Elevator.Elevator(String,int) 1 1 1
homework.custumer.Elevator.close() 1 1 1
homework.custumer.Elevator.getCurrentFloor() 1 1 1
homework.custumer.Elevator.getMinDisReq(int,int) 1 1 1
homework.custumer.Elevator.getStatus() 1 1 1
homework.custumer.Elevator.inOne(Person) 1 3 3
homework.custumer.Elevator.isEmpty() 1 1 1
homework.custumer.Elevator.isFull() 1 1 1
homework.custumer.Elevator.move(boolean) 1 1 1
homework.custumer.Elevator.open() 1 3 3
homework.custumer.Elevator.outOne() 1 1 1
homework.custumer.Elevator.setStatus(Status) 1 1 1
homework.custumer.Elevator.sleepTime(EleTime) 2 2 5
homework.custumer.Elevator.updateCurrentFloor(boolean) 1 1 4
homework.custumer.ElevatorController.ElevatorController(Scheduler,String,int) 1 1 1
homework.custumer.ElevatorController.eleClose(int) 1 3 3
homework.custumer.ElevatorController.eleClosed(int,boolean) 1 5 6
homework.custumer.ElevatorController.eleOpen(int) 3 4 4
homework.custumer.ElevatorController.eleWait(int,boolean) 2 5 5
homework.custumer.ElevatorController.getGoalFloor(int,boolean) 4 3 5
homework.custumer.ElevatorController.run() 2 5 12
homework.custumer.ElevatorMul.ElevatorMul(int,int[]) 1 2 2
homework.custumer.ElevatorMul.convertIn(int) 3 1 3
homework.custumer.ElevatorMul.convertOut(int) 2 1 2
homework.custumer.ElevatorMul.getMinDisReq(int,int) 8 3 12
homework.custumer.ElevatorMul.inOne(Person) 1 1 1
homework.custumer.ElevatorMul.isEleEmpty() 2 1 2
homework.custumer.ElevatorMul.isEleFull() 2 1 2
homework.custumer.ElevatorMul.mulOut(int) 1 2 2
homework.custumer.ElevatorMul.outOne(int) 2 2 2
homework.custumer.ElevatorMul.updateEleSize(boolean) 1 1 2
homework.producer.RequestParser.RequestParser(Scheduler) 1 1 1
homework.producer.RequestParser.run() 3 5 6
homework.tray.Person.Person(PersonRequest) 1 1 1
homework.tray.Person.eleIdNull() 1 1 1
homework.tray.Person.equals(Object) 2 1 2
homework.tray.Person.getEl() 1 1 1
homework.tray.Person.getFr() 1 1 1
homework.tray.Person.getId() 1 1 1
homework.tray.Person.getMoveDis() 1 1 1
homework.tray.Person.getTo() 1 1 1
homework.tray.Person.getTransfer() 1 1 1
homework.tray.Person.getWaitTime() 1 1 1
homework.tray.Person.hashCode() 1 1 1
homework.tray.Person.makePersonIn() 1 1 1
homework.tray.Person.makePersonOut() 1 1 1
homework.tray.Person.moveUp() 1 1 1
homework.tray.Person.sameForward(boolean) 1 1 1
homework.tray.Person.setEl(String) 1 1 1
homework.tray.Person.setFr(int) 1 1 1
homework.tray.Person.setTransfer(int) 1 1 1
homework.tray.Person.setWaitTime(int) 1 1 1
homework.tray.Person.toString() 1 1 1
homework.tray.Person.transferPerson(int) 1 1 1
homework.tray.Scheduler.Scheduler() 1 1 1
homework.tray.Scheduler.failToGetOne(int,String,int) 1 1 1
homework.tray.Scheduler.getMinDis(int,int,String,int) 1 1 1
homework.tray.Scheduler.getMinDisWhenEmpty(int,String,int) 2 5 6
homework.tray.Scheduler.getOne(int,int,String,int) 1 1 1
homework.tray.Scheduler.hasRequestInFloor(int,String,int) 1 1 1
homework.tray.Scheduler.readNewRequest(Person) 1 1 1
homework.tray.Scheduler.stopReading() 1 1 1
homework.tray.Scheduler.stopTask() 1 1 1
homework.tray.Scheduler.tranOfTypeIsEmpty(int) 1 1 1
homework.tray.Scheduler.updateFlag(int,String,int) 1 1 1
homework.tray.WaitMul.WaitMul() 1 3 3
homework.tray.WaitMul.addOutsideMul(Person) 3 2 5
homework.tray.WaitMul.canStopIn(int,int) 1 1 1
homework.tray.WaitMul.convertIn(int) 3 1 3
homework.tray.WaitMul.convertOut(int) 2 1 2
homework.tray.WaitMul.failToGetOne(int,String,int) 3 1 3
homework.tray.WaitMul.getBestOne(int,int,String,int) 1 3 3
homework.tray.WaitMul.getDisOfTran(int,int,int) 1 1 1
homework.tray.WaitMul.getEleType(int) 3 1 3
homework.tray.WaitMul.getInsideTransfer(int,int) 4 4 14
homework.tray.WaitMul.getInsideTransfer(int,int,int,int) 2 3 4
homework.tray.WaitMul.getMinDisFloor(int,int,String,int) 2 4 4
homework.tray.WaitMul.getMinDisPerson(int,int,String,int) 4 2 8
homework.tray.WaitMul.getMinDownFloorReq(int,int,String,int) 3 2 3
homework.tray.WaitMul.getMinUpFloorReq(int,int,String,int) 3 2 3
homework.tray.WaitMul.getTranOfType(int) 3 2 3
homework.tray.WaitMul.hasRequestInFloor(int,int,String,int) 2 1 2
homework.tray.WaitMul.isEmpty() 3 2 3
homework.tray.WaitMul.mustTakeIn(int,int) 1 1 1
homework.tray.WaitMul.removeOneInTran(int) 1 3 3
homework.tray.WaitMul.searchInFloor(int,int,int,String,int) 6 7 12
homework.tray.WaitMul.tranOfTypeIsEmpty(int) 1 2 2
homework.tray.WaitMul.tranPut(int,int,int) 1 4 4
homework.tray.WaitMul.tranWait(int,int) 2 1 3
homework.tray.WaitMul.updateFlag(int,String,int) 1 4 4

类复杂度也一样。

ClassOCavgWMC
homework.MainClass 1 1
homework.SafeOutput 1 1
homework.custumer.Controller 1.5 6
homework.custumer.Elevator 1.57 22
homework.custumer.Elevator.EleTime n/a 0
homework.custumer.Elevator.Status n/a 0
homework.custumer.ElevatorController 4.29 30
homework.custumer.ElevatorMul 2.9 29
homework.producer.RequestParser 3 6
homework.tray.Person 1.05 22
homework.tray.Scheduler 1.18 13
homework.tray.WaitMul 3.24 81

 

四、三省吾身

4.1 第五次作业

本次作业在强测中没有出现Bug,在互测中没有被Hack。

 

4.2 第六次作业

本次作业在强测中没有出现Bug,在互测中没有被Hack。

 

4.3 第七次作业

本次作业在强测中没有出现Bug,在互测中被Hack一次。

这次很惨地被Rider暴捶,Rider实在是一个居住在OO网站上的狠人,最终战绩30 / 168,从头到尾穷追不舍Berserker,hack了我试了好几次也打不到的Alterego(我本地会测出他有死锁的情况),用运行超时死锁虐了我和Saber……

Rider从头到尾就在提交一个数据(除了固定欺负Berserker,还会捎带暴捶我们):

 [1.0]651-FROM-5-TO-15
 [1.0]404-FROM-9-TO-1
 [5.0]797-FROM-10-TO-1
 [5.0]75-FROM-2-TO--2
 [5.0]816-FROM-1-TO-7
 [5.0]699-FROM-9-TO--3
 [9.0]46-FROM-11-TO-2
 [9.0]592-FROM--3-TO--2
 [9.0]152-FROM-1-TO--3
 [9.0]480-FROM-1-TO-2
 [9.0]385-FROM--3-TO-6
 [9.0]199-FROM--3-TO-1
 [9.0]632-FROM-13-TO-20
 [9.0]562-FROM-6-TO-12
 [9.0]72-FROM-9-TO-4
 [13.0]995-FROM-1-TO--2
 [13.0]977-FROM-1-TO-12
 [13.0]633-FROM-1-TO-3
 [13.0]176-FROM-1-TO-11
 [13.0]865-FROM-2-TO-1
 [13.0]471-FROM-8-TO-1
 [13.0]594-FROM-11-TO-4
 [13.0]765-FROM-3-TO--3
 [13.0]478-FROM-1-TO--3
 [17.0]357-FROM-1-TO-11
 [17.0]604-FROM-9-TO-8
 [17.0]447-FROM-1-TO-10
 [17.0]106-FROM-9-TO-14
 [17.0]194-FROM-7-TO-1
 [17.0]387-FROM-12-TO-1
 [17.0]760-FROM-1-TO-14
 [17.0]X1-ADD-ELEVATOR-C
 [17.0]874-FROM-16-TO--3
 [17.0]369-FROM-1-TO-9
 [17.0]268-FROM-8-TO--3
 [17.0]498-FROM--3-TO-5
 [17.0]340-FROM-10-TO-4
 [17.0]859-FROM-1-TO-8
 [17.0]423-FROM-1-TO-14
 [17.0]211-FROM--1-TO-2
 [21.0]518-FROM-17-TO-14
 [21.0]242-FROM-10-TO-1
 [25.0]871-FROM--3-TO-6
 [25.0]427-FROM--1-TO-1
 [25.0]X2-ADD-ELEVATOR-A
 [29.0]975-FROM-14-TO-1
 [29.0]504-FROM-1-TO--1
 [29.0]942-FROM-1-TO--1
 [29.0]96-FROM-1-TO--1
 [29.0]133-FROM--3-TO-1
 [29.0]558-FROM-15-TO--3
 [29.0]X3-ADD-ELEVATOR-A

单看数据,这组数据的特点是分几个时间段,在同一时刻进行大量乘客投放和电梯增加操作。

那么为什么会发生死锁呢?相信我们房间的同学都很想了解为什么会发生死锁,小编对于会发生死锁这件事也感到很惊讶,但是就是发生死锁了,那么这就是发生死锁的故事了。对于这件事情大家有什么看法呢,欢迎在评论区留言哦。

……言归正传……我本地将这个数据跑了1000次,也没有成功复现。而根据课程组提供的加密输出,可以发现产生死锁的样例输出中间有明显间隔期,然而CPU时间为1.2s,说明没有发生暴力轮询,而我所采用的架构是人类在每次电梯发生楼层改变时都会重新呼叫当前最近的电梯,所以……我是真的想不出来原因了……解决办法就是给wait价格时间限制试试?但我也不清楚这个的作用,毕竟直接再交一遍也过了……

以上,互测被扣分了是我能力不够,等解禁了一定要加Rider膜拜一下,对于这个问题我也不会忘记的,以后姿势水平提升了要努力弄明白。

 

五、互测策略

第五、六次没有发现他人Bug,但是第六次有房间成员发现他人Bug……第七次发现3处Bug,Hack成功2处。

在互测时发现了别人的线程不安全Bug:

  • 调度器类使用for each遍历当前电梯列表,如果这时出现新的电梯添加请求(迭代过程中进行了修改),就会抛异常。(提交4次数据成功1次)

而互测策略上,这次就没有对拍……多线程主要依赖自动测试,感谢HDL学长的定时输入轮子和某位学长的自动化测试机和WPB的多线程评测机。这里再次膜拜一下一个样例提交20多次的居住在OO网站上的Rider。

一些卡时间点的数据我试了下无人中招……毕竟对自己的性能有一点自信,所以当时互测就没有深入测试……这里再次膜拜一下卡RTLE的居住在OO网站上的Rider。

 

六、心得体会

从难度上来说,我觉得第二单元比第一单元略小。第一单元为了性能和正确性,需要考虑的东西还挺多的。而虽然说相比去年今年第二单元的进度提早、需求增加,但是有了第一单元学习到的经验,迭代开发使得三次作业的改进更加方便。

这一单元关于锁的概念理解与实际操作非常重要,我主要使用synchronized关键字修饰单一类的方法,发生死锁的概率较低。而多线程也对自动化测试有了更高的要求,之后也要强化Python编程能力。

不过这三次还是花了很多时间在修复Bug上,希望下次可以避免。

posted @ 2020-04-18 11:05  MarkDay  阅读(982)  评论(0编辑  收藏  举报