面向对象第二单元(多线程调度)总结体会
面向对象第二单元(多线程调度)总结体会
前言
这三次作业的训练目的在于熟练掌握JAVA多线程同步互斥调度。其采用了比较贴合实际的电梯问题来作为载体,将多线程调度由易到难分成三个部分:
第一阶段要求了解结合多线程概念,理解电梯与请求之间的协作关系,考虑哪些资源需要被两个进程同时访问
第二阶段要求不再是单方向的信息传递,而是两个方向的信息交互,尤其注意交换信息时的同步关系
第三阶段要求采用更加科学高效的调度算法,同时满足电梯与电梯之间的互斥,电梯与请求之间的同步
1.第五次作业(FAFS电梯模拟)
1.1 程序类图展示

1.2 程序结构分析
Mainclass为主类,通过创建Request与 Elevator两个线程来实现功能。
使用的是“生产者-消费者”模型,将请求队列存在queue 中,而Request与 Elevator一个向queue中加入请求,一个从queue中得到请求。
请求的加入: put(PersonRequest):
用来向ArrayList请求队列中添加一个请求,该函数使用synchronized与Elevator进行互斥。
请求的取出: get():
用来从ArrayList请求队列中取出一个请求,若queue为空,则将Elevator进程设为阻塞态。该函数使用synchronized与Request进行互斥。
请求的结束:setOver(Boolean):
当Request进程死亡时,将over位设为true,同时唤醒可能在睡眠中的Elevator进程。该函数使用synchronized与Elevator进行互斥。
1.3 优缺点分析
优点:此次作业比较完全地使用了生产者-消费者模式,而没有将调度放在电梯当中。类之间的耦合度较低,通过JAVA自带的synchronized实现了进程之间的互斥,完成了信息的共享与交换。
缺点:没有在此基础上使用更高级的调度算法,而无法实现人员的捎带。程序实现的是实打实的FAFS模式
1.4 设计原则检测
- Single Responsibility Principle:类/方法的功能行为单一,电梯仅根据需求执行上下功能,符合该原则。
- Open Close Principle:一定程度上考虑了可拓展性,但依照开闭原则应该进一步增加框架弹性。
- Liscov Substitution Principle:无子类无继承,满足里氏替换原则。
- Interface Segregation Principle:无接口设计,满足接口分离原则。
- Dependency Inversion Principle:调度器(高层模块)不依赖于电梯(低层模块)的行为,符合该原则。
1.5 代码度量
复杂度分析

依赖度分析

本次代码的整体复杂度较低,代码规模较小,依赖度也没有超过2。整体代码量只有100+行,实现模式较为简易。
1.6 UML协作图

1.7 Bug分析
第一次程序所在房间内均为满分。既没有被找到bug,也没有找到别人bug
通过读同房间其他同学的代码,发现由于指导书要求较为明确,观察到许多同学的思路基本相同
最大的区别在于是否将控制器作为一个线程运行。
若不作为一个线程,则是标准的生产者-消费者模式;若作为一个线程,则类似于观察者模式。
2.第六次作业(单部可捎带调度(ALS)电梯)
2.1 程序类图展示

2.2 程序结构分析
从类图可以看出,第二次程序类图与第一次大体不变,主要的改动为:
- 在
Controller中加入了getMainNeed()方法 - 在
Elevator中加入了mainNeed与isOpenDoor变量,并定义了相关的getter and setter方法
当电梯内没有人时,访问Controller来取得主请求;当电梯在运行时,每到一层先遍历电梯中的所有人员,观察是否需要开门出去,之后更新主请求;然后访问Controller来了解是否需要上人,再次更新主请求。最后根据主请求上行或者下行。
2.3 优缺点分析
优点:在上次的基础上复用了较多的代码,证明整个框架的设计比较合理,且可以按照指导书的要求进行运行。
缺点:本次仅仅实现了AFS调度,而没有想到更高效的方案,使得性能分部分爆炸;在负一层与一层之间转移时使用了特判,导致之后第三次作业还需重新构思相关设计。
2.4 设计原则检测
- Single Responsibility Principle:电梯仅根据需求执行上下功能,而采用询问的方法获得主请求,符合该原则。
- Open Close Principle:一定程度上考虑了可拓展性,但在楼层转移上没有考虑复用而偷懒特判,需要反思。
- Liscov Substitution Principle:无子类无继承,满足里氏替换原则。
- Interface Segregation Principle:无接口设计,满足接口分离原则。
- Dependency Inversion Principle:控制器(高层模块)的调度依赖于电梯(低层模块)的行为,不符合该原则。
2.5 代码度量
复杂度分析


依赖度分析

复杂度分析中,两个最重要的方法复杂度明显大于其他方法,由于首先需要将queue遍历一遍,并在其中判断,在循环中还要将不同的情况分类讨论,故需要多个if语句,所以整体复杂度要高于其他方法。而依赖度关系情况良好,类与类之间的依赖度关系比较稳定。
2.6 UML协作图

2.7 Bug分析
此次作业强测没有被找到bug,同时也没有找到他人bug。虽然性能分近乎爆炸,但可靠性得到了保障。测试他人bug的时候,使用Random类与正则表达式随机生成了一些数据,并利用.sh文件与管道实现了自动化评测,但各个同学的调度算法均不同,无法通过肉眼观察是否输出内容是否正确,只能通过观察是否超时来计算。但由于效率比较低下,并没有在有限的时间内找到有效的bug提交。
3 第七次作业 (多部智能(SS)调度电梯)
3.1 程序类图展示

3.2 程序结构分析
从程序类图可以发现,本次结构与上次最大的不同即为加入了一个新的Scheduler类来完成调度任务。而输入接口接入的不再是Controller,而是Scheduler。而Controller变成了每台电梯的附属控制器,用于管理控制已经分配给每台电梯管理请求的捎带。具体的调度策略如下所示:
Scheduler通过判断请求是否可以由一部电梯独立完成,若可以则按照空闲>A>B>C的策略分配电梯- 若无法通过一部电梯到达目的地,则将请求拆分为两个,而中间的过渡楼层的判断以“运行速度快的电梯多运行,运行速度慢的电梯少运行”的原则进行选择。然后将请求的第一部分按照上一步的原则分配电梯,将请求的第二部分存在
Scheduler的Hashmap中,并用PersonRequest的id作为Key。 - 控制器将调度器给它的请求存入自己的
queue,并按照上次作业的算法进行电梯的捎带请求。唯一改变的是,当电梯人数已满的情况下,不进行人员进入的操作。
3.3 优缺点分析
优点:在每台电梯的内部调度部分几乎复用了所有代码,证明该框架的复用性良好;使用比较高效的静态调度算法,而由于感觉动态调度算法会破坏“高内聚,低耦合”的原则,而没有考虑使用。
缺点:在代码中有些数据可以GET INSTANCE方式来调取使用,但却使用了调取Elevator类中的私有类变量,提高了类与类之间的耦合度关系。在判定换乘时,按照最多换乘一次的方法进行设计的,而没有采用图论的最短路径算法,这样使得代码的复用性极差,若有下次作业,Scheduler中的调度或许需要完全重构。
3.4 设计原则检测
- Single Responsibility Principle:调度器计算拆分需求并交给电梯控制器,电梯仅根据需求执行上下功能,而采用询问的方法获得主请求,符合该原则。
- Open Close Principle:在电梯的换乘中没有按照构造图的普适方式实现,而使用了换乘一次的方法,需要反思。
- Liscov Substitution Principle:无子类无继承,满足里氏替换原则。
- Interface Segregation Principle:无接口设计,满足接口分离原则。
- Dependency Inversion Principle:调度器,控制器(高层模块)的调度依赖于电梯(低层模块)的行为,不符合该原则。
3.5 代码度量
复杂度分析


依赖度分析

可以观察到,本次代码中复杂度较高的函数除了上次作业中的get与getMainNeed,还有调度器中的isSingle(单电梯调度)和isDouble(双电梯调度),与预料中的基本相似。而依赖度关系的上升,很大程度上是由于在数据选择上采取使用Elevator中的数据,而没有GET INSTANCE,需要反思。
3.6 UML协作图

3.7 Bug修复
本次强测还是没有被找到bug,也没有找到别人bug。强测的性能分较上次而言,有所提高。在寻找他人bug的时候,考虑到同房间同学的分数与我相近,仅采用了暴力测试,观察程序是否超时这一方式,而没有采用更加明智的方法。
总结
本三次作业在很大程度上使我理解了JAVA多线程编程的特点,对线程之间的同步互斥关系有了更深入的认识。在代码复用上,对比较第一次作业而言进步巨大,基本上后一次作业对前一次作业达到80%以上的复用,但在多线程调试以及测试bug中还没有做到自动化测试的效果,期待在后续的时间中,通过自学和老师的帮助,可以对多进程的其他编程思想以及测试有更高层次的理解。期待下一单元的OO练习ing!

浙公网安备 33010602011771号