目录
文件结构
多线程的协同和同步控制
功能设计和性能设计
程序结构与面向对象
三次作业遇到的BUG
心得体会
文件结构
| .gitignore
| hw2.iml
| pom.xml
|
+---course
| elevator-input.jar
| timable-output.jar
|
+---src
| +---main
| | +---java
| | | | Main.java
| | | | Tester.java
| | | |
| | | +---building
| | | | Bridge.java
| | | | Building.java
| | | |
| | | +---elevator
| | | | Driver.java
| | | | Elevator.java
| | | | ElevatorElementPath.java
| | | |
| | | +---goods
| | | | Goods.java
| | | | GoodsStage.java
| | | |
| | | +---interfaces
| | | | BuildingInterface.java
| | | | Callback.java
| | | | DriverInterface.java
| | | | ElevatorInterface.java
| | | | PlanInterface.java
| | | |
| | | +---scheduler
| | | | MinPath.java
| | | | NeedStop.java
| | | | PlanHelper.java
| | | | Scheduler.java
| | | |
| | | \---utils
| | | Helper.java
| | | MyReq.java
| | | Timer.java
| | |
| | \---resources
| \---test
| \---java
+---testIn
| in1
| in2
| in3
|
\---testOut
out1
out2
out3
多线程的协同和同步控制
本次作业主要有Main,Scheduler,CallbackHelper,Driver四种进程。Callback是为了鲁棒性设计的,在每个Driver进程自然结束时调用。其他的比较好理解,Main启动Scheduler,Scheduler启动Driver。
//Scheduler
@Override public void callback() { new Thread(this::startNew, "callback start helper of " + Thread.currentThread().getName()).start(); }
在新人到来时立即尽快结束所有Driver,Scheduler重新规划。
//Driver
@Override public void run() { Helper.print(getName() + " start running"); for (ElevatorElementPath p : path) { if (stop) { ele.stop(); return; } switch (p) { 。。。 } if (stop) { ele.stop(); return; } if (p == ElevatorElementPath.OPEN) { break; } } call.callback(); } @Override public void tryStop() { stop = true; }
同步控制上所有集合全部进行了上锁,部分基本类型进行了封装。
public class NeedStop { private int waiting = 0; synchronized int add() { return ++waiting; } synchronized int del() { return --waiting; } synchronized boolean can() { Helper.myAssert(waiting > 0, "impossible waiting val"); return waiting == 1; } }
功能设计与性能设计
功能设计上,我使用静态确定人的路径,动态确定电梯路径。人的路径由MinPath规划(实现了spfa求最短路),电梯路径由Scheduler规划(原则是每个方向上第一个电梯先取,先取位置最远的并且方向是这个方向的人),规划结果存储在PlanHelper中。
安全性上,由于每个类功能很专一所以自认为做的很好,建立了Bridge进行人的交换,而Elevator保证了电梯运行不会出逻辑错误。
性能上由于是随机数据,所以只要电梯差得开,加LOOK算法,就可以踩到时间下限(一个电梯是最后到达的人的时间+电梯上下总时间),事实上如果过度优化一般连这个都达不到。
(红色是进,绿色是出,第二次的middle6,自己写的绘电梯运行图工具)
本次作业我的可拓展性也很好,尤其是算法我的解耦很好,能够比较好的适应策略的更换。countForInEle,countForInBud1,countForInBud2,(参数包括电梯,楼信息,待规划人集合)分别实现了对电梯内,楼里的人的多种路径规划方式。而main中则是策略的设计界面,通过对上面的函数的简单堆砌就可以描述出你想要的策略。
程序结构与面向对象
class |
OCavg | WMC |
main.java.building.Bridge |
1.7692307692307692 | 23.0 |
main.java.building.Building | 1.5 | 9.0 |
main.java.elevator.Driver | 3.5 | 14.0 |
main.java.elevator.Elevator | 1.65 | 33.0 |
main.java.elevator.ElevatorElementPath | 0.0 | |
main.java.goods.Goods | 1.125 | 9.0 |
main.java.goods.GoodsStage | 1.0 | 3.0 |
main.java.Main | 7.0 | 7.0 |
main.java.scheduler.MinPath | 2.076923076923077 | 27.0 |
main.java.scheduler.NeedStop | 1.0 | 3.0 |
main.java.scheduler.PlanHelper | 1.2 | 12.0 |
main.java.scheduler.Scheduler | 2.4285714285714284 | 68.0 |
main.java.Tester | 3.0 | 9.0 |
main.java.utils.Helper | 1.8125 | 29.0 |
main.java.utils.MyReq | 1.25 | 10.0 |
main.java.utils.Timer | 1.7142857142857142 | 12.0 |
Total | 268.0 | |
Average | 1.8741258741258742 | 16.75 |
可以看到设计的是非常优秀的。相比于很多同学,我完全没有出现循环依赖。这是充分利用了接口的结果。
类的复杂度也比较的低。其中Bridge的设计分散了elevator,和building的功能我觉得很成功。
缺点就是类之间的依赖程度还是比较高的,当时没有关注这方面的解耦。
三次遇到的BUG
三次互测均没有被测出bug。
第二次强测出现了人没有被处理的情况。原来是map线程不安全,导致人没有被加到考虑集中。
编程时在多线程上我遇到过一次死锁。原来是我的scheduler在定策略时会锁住自己并暂停Driver,没有callbackhelper,有的Driver也会走到callback定策略这最后一步,由于无法获得锁,程序死锁。
心得体会
这次作业花费了比较大的心思做架构和优化设计。架构因此比求导好了很多。但我仍认为还有很大空间,并得到一个经验:不会划分功能,就别瞎划分。优化走了很多弯路,最后悟出没有实验就没有发言权,遂把电梯路径画了出来,最终自认为摸到了真理(因为指导书说了是随机数据,分散+LOOK就是最优),可惜没有心力去实现了。
最后遗憾就是maven的架子都搭好了,但还是没时间做测试。