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


一、设计策略

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启动输入线程和电梯线程,外部的楼层等待人群和内部的电梯货运人群采用了继承抽象类人群的方式,将对人的存储方式封装起来。

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

▼ 依赖关系,还可以。

Class Cyclic Dcy Dcy* Dpt Dpt*
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方法这次写的很复杂……

Method ev(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.MainClass 1 1
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启动输入线程和多个电梯线程,这次放弃了继承,并且优化了传递人员的方式,降低了耦合性。

▼ 时序图。

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

Class Cyclic Dcy Dcy* Dpt Dpt*
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中用了大量循环遍历比较获取最小值,基本复杂度和圈复杂度都比较差。

Method ev(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设计由于放弃了继承关系,也不是很乐观。

Class OCavg WMC
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的封装,虽然不加也没什么影响……

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

Class Cyclic Dcy Dcy* Dpt Dpt*
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

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

Method ev(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

类复杂度也一样。

Class OCavg WMC
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上,希望下次可以避免。

2020 OO 第二单元总结的更多相关文章

  1. 2020北航OO第二单元总结

    2020北航OO第二单元总结 前言 本单元考察基于多线程的电梯调度问题,成功让我从一个多线程小白到了基本掌握了使用锁来控制线程安全的能力,收获颇多(充分体验了迷茫地de一个又一个死锁bug的痛苦). ...

  2. oo第二单元作业总结

    oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...

  3. OO第二单元优化博客

    OO第二单元优化博客 第五次作业没有性能分,但是,我在这一单元的宗旨就是写一个日常生活中 最常见的那种电梯,所以第五次我没有写傻瓜电梯,而是直接写了个\(look\),和第六次基本相同. 总计一下lo ...

  4. 【OO学习】OO第二单元作业总结

    OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...

  5. OO第二单元小结

    OO第二单元小结 一.三次作业代码分析. 1.第一次作业 第一次作业是单部电梯的傻瓜调度,由于其过分傻瓜,所以第一次作业我只有两个类,一个main,一个电梯,main类负责不断从输入流中读取命令,如果 ...

  6. OO第二单元多线程电梯总结

    OO第二单元多线程电梯总结 第一次作业 设计思路 Input为输入线程,负责不断读取请求并将读到的请求放入调度器中. Dispatcher为调度器,是Input线程和Elevator线程的共享对象,采 ...

  7. 电梯也能无为而治——oo第二单元作业总结

    oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...

  8. OO第二单元——多线程(电梯)

    OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...

  9. OO第二单元作业总结【自我反思与审视】

    第二单元作业的完成史,就是一部心酸的血泪史…… 多线程的出现为我(们)打开一片广阔的天地,我也在这方天地摸爬滚打,不断成长!如果说第一单元之前还对Java语法有所了解的话,那么这单元学习多线程则完全是 ...

随机推荐

  1. Dart: 编码和解码各种存档和压缩格式

    path archive import 'dart:io'; import 'package:path/path.dart' as p; import 'package:path/path.dart' ...

  2. 25_MySQL 数据操作语言:UPDATE语句

    -- UPDATE 把每个员工的编号和上司的编号都加1,用 ORDER BY 完成 UPDATE t_emp SET empno=empno+1,mgr=mgr+1 ORDER BY empno DE ...

  3. 解决java POI导入Excel超时问题

    由于要导入大量数据,后台会耗费很长时间,导致超时. 本项目前端request.js中设定的超时时间为150s. const service = axios.create({ baseURL: base ...

  4. 【HTB系列】靶机Netmon的渗透测试

    出品|MS08067实验室(www.ms08067.com) 本文作者:是大方子(Ms08067实验室核心成员) 总结和反思: win中执行powershell的远程代码下载执行注意双引号转义 对po ...

  5. Nginx解析漏洞复现以及哥斯拉连接Webshell实践

    Nginx解析漏洞复现以及哥斯拉连接Webshell实践 目录 1. 环境 2. 过程 2.1 vulhub镜像拉取 2.2 漏洞利用 2.3 webshell上传 2.4 哥斯拉Webshell连接 ...

  6. FreeBSD 将降低对 i386 架构的支持力度

    FreeBSD 开发团队宣布,从 FreeBSD 13.0 开始,对 i386 架构的支持级别将降级为 Tier 2,未来的 14.0 可能还将会在此基础上进一步降低对 i386 架构的支持.而对于 ...

  7. (Java基础--Spring阶段)常见面试题题目及解析整理(2021.03.12)

    题目整理 Java基础进阶阶段 基础概念类 1.JDK1.8新特性? 2.面向对象和面向过程的区别? 3.什么是值传递和引用传递? 4.什么是不可变对象? 5.讲讲类的实例化顺序? 6.java 创建 ...

  8. vue 倒计时 iOS无效

    vue实现的倒计时在苹果手机上无效,原因是因为后台返回的时间格式是'2019-1-29 17:13:04',而苹果手机只能解析这种时间格式'YYYY/MM/DD HH:mm:ss',修改后测试成功的代 ...

  9. 设计模式之建造者模式(BuilderPattern)

    一.意义 将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 说明:复杂对象的构建,比如一个对象有几十个成员属性,那么我们在创建这个对象,并给成员属性赋值时,就会很麻烦.采用 ...

  10. IDEA的注册方式

    http://idea.lanyus.com/ 使用前请将"0.0.0.0 account.jetbrains.com"添加到hosts文件中 hosts文件在C:\Windows ...