标签:集合 9.png 不同 特点 nts 疑问 限制 系统 分析
? 多线程(multi-threading)是指从软件或硬件上实现多个线程并发执行的技术。现代处理器普遍具有多核的特点,支持同一时间执行多个线程,使用多线程技术可以提高程序并发度,整体提高处理性能。因此掌握多线程程序设计技术是CS学习必不可少的一部分。
? 多线程程序设计包括线程协同控制、线程安全保证以及线程程序设计模式等。本文主要总结OO课程第二单元所学的有关多线程程序设计的知识,实践体会,以及技术注记,以备后用。
? 第五次作业实现了一个单部多线程傻瓜调度(FAFS)电梯,目的在于初步掌握JAVA语言有关多线程的语法特性,同时进行初步的可拓展的框架设计
? 第一次作业主要是让我们初步熟悉JAVA的多线程相关语法,主要考察的知识点是简单的线程协同控制,对于其他诸如线程安全问题等没有太多要求,因此整体比较简单。
1.1 代码结构分析
? 由上述类图可以知道,整个电梯系统可以分为以下模块:
InputHandler
: 指令输入管理器。负责接收指令输入,并将指令解析成PersonRequest对象,传给调度器Scheduler
Scheduler
:调度器。负责接收输入管理器传入的请求,同时进行调度管理(在这次作业仅是将请求传给该单部电梯)Elevator
:电梯。负责接收调度器传入的请求,并通过一定的逻辑执行请求,具有电梯属性和行为特征(如开关门,上下楼等)OutputHandler
:信息输出器。负责信息输出。如开关门、上下楼、乘客进出等信息输出。? 除此之外,还有与电梯系统相关的数据结构:
RequestQueue
:请求队列。负责存放请求,由ConcurrentLinkedQueue
实现,作为InputHandler
和Scheduler
、Scheduler
和Elevator
的共享对象存在,是线程安全类。ElevatorConfig
:电梯属性类。负责初始化电梯属性,具有拓展性。1.2 优缺点分析
输入管理器->调度器->电梯
的大体框架较为简单易懂且易实现。ConcurrentLinkedQueue
线程安全队列,一定程度上简化了读-写实现,减轻了对线程不安全的考虑负担。调度器->电梯
的映射,出于后续作业可能的多部电梯的考虑。Elevator
类的逻辑可以进一步简化,特别是在后续作业中电梯功能进一步增多时,这一点可以帮助降低Elevator
类的复杂度,降低bug出现的概率。ALS
、C-SCAN
、C-LOOK
等电梯调度算法。wait/notify
来避免无谓的轮询:仅使用了暴力轮询来完成功能,导致CPU负载较高(虽然本次作业并没有对此做出要求)。1.3 设计原则检查
? (度量符号说明参见注记,下面对个别新增度量进行注释)
? LOC
:Lines of Code: 类/方法代码规模
? NAAC
:Number of Attributes: 类属性个数
? NOAC
:Number of Methods:类方法个数
? CONTROL
:Number of Control statements:控制分支个数
代码统计
类度量
类复杂度度量
方法复杂度度量
类整体复杂度较低,方法整体复杂度较低。
? 在中测和强测均得到满分,暂时未发现bug
? 补充说明:(自测过程中的发现)
线程安全问题:
在本次作业的实现过程中,由于使用了ConcurrentLinkedQueue
线程安全队列,以及使用暴力轮询,因此并没有显式使用synchronized
或Reentranlock
等JAVA锁机制来保证线程安全。由于任务简单,因此没有出现bug,但事后来看还是应该进行显式使用锁机制,一是为了避免未知不定的线程不安全,二是在可读性和代码含义上显式使用锁机制能够起标识警示作用。
测试问题:
多线程程序设计的另一大难点就是调试。由于线程执行的不确定性以及程序调试断点的特殊性(断点的设置可能会影响线程的行为),因此多线程程序的测试比单线程程序更加繁琐。
? 第一次作业比较友好,主要是起铺垫作用,整个实现过程也比较顺利,耗时较短。同时自己也初步尝试了多线程模式进行程序设计,但整体来看对线程安全以及CPU使用考虑不够。特别是在拓展方面,其实应该提前考虑电梯调度算法,减轻后续作业的负担。不仅如此,在测试方面可以做得更好,毕竟多线程的debug没有单线程那么简单易行,涉及到时序,使用脚本对拍是一个更好的方法。
? 第六次作业要求实现一个单部多线程可捎带调度(ALS)电梯,目的在于进一步拓展单部电梯的功能,加强面向对象设计与并发设计训练,重点关注代码逻辑复杂情况下线程并发控制与线程安全保护。
? 第六次作业的基本框架沿袭了第五次作业,同时针对新增的捎带功能进行了一定的拓展。
1.1 代码结构分析
? 新增了以下类:
ElevatorController
:电梯控制类。封装了部分电梯执行逻辑,如返回下一楼层,返回运动方向,捎带检测等逻辑。? 修改了以下类:
RequestPack
:用于下面的楼层建模,为捎带功能服务。主要包含两个队列,一个为进入队列,一个为离开队列,对应于某个楼层。即当前在某楼层有多少要进入电梯,有多少要离开电梯的人。
Elevator
:对电梯的行为进行进一步细化与拓展;增加了与捎带策略有关的数据结构,具体如下:
RequestQueue
:请求队列:与调度器共享,负责接收调度器传入的请求,需要保证线程安全。MainRequestQueue
:主请求队列:用于存放主请求,属于对象内部变量,不需要保证线程安全。具体作用是,当请求不能被当前主请求捎带时则先加入该队列暂存。packMap
:捎带映射Map:相当于楼层建模,key
为楼层号,value
为RequestPack
类。? 总体来说继承了第五次作业的代码框架,同时进行了一定的拓展,重点在于捎带功能的实现。捎带主体逻辑可以拆分成相关数据结构和相关算法:
1.2 优缺点分析
InputHandler
、OutputHandler
等与新增功能无关的模块重用,仅在Elevator
类中增加逻辑,整体改动较少。C-LOOK
,C-SCAN
等算法可以提高电梯运行效率。1.3 设计原则检查
? (度量符号说明参见注记,下面对个别新增度量进行注释)
? LOC
:Lines of Code: 类/方法代码规模
? NAAC
:Number of Attributes: 类属性个数
? NOAC
:Number of Methods:类方法个数
? CONTROL
:Number of Control statements:控制分支个数
代码统计
类度量
类复杂度度量
方法复杂度度量
部分类(Elevator
等)复杂度较高,一部分原因是其组合的类较多,另一部分原因则是其逻辑较为复杂,需要进一步细化功能。
? 在强测中出现了bug,错误信息显示是存在乘客未送到目的楼层;经代码检查后发现是捎带逻辑中的一个变量名写成了另一个变量名,导致部分捎带未能存入捎带map后未能响应。
? 本次作业在强测上较为可惜,主要责任在于自己未能进行全面的测试,特别是基本的覆盖分析也没有仔细进行,导致出现bug,引以为戒。
? 第六次作业在第五次作业的基础上增加了对电梯调度性能的要求,实际上在以下几方面对我们进行了考核:
? 从以上角度看,本次作业较好地完成了前两点,在第三点上还需要进一步改进,因为本次作业出现的bug一部分原因也是在捎带算法的逻辑复杂上,除此之外,高效的调度算法实现起来反而可能比低效的算法简单,因此后续作业尽量在性能方面多投入些精力。
? 第七次作业要求实现多部多线程智能(SS)调度电梯,进一步考察面向对象多线程程序的设计能力,重点关注多线程协同、线程安全与面向对象抽象设计、拓展设计的结合考察。
? 第七次作业在第六次作业的基础上进行了改动,目的是为了拓展以下功能:
1.1 代码结构分析
? 针对上述需要拓展的功能分别介绍本次作业代码结构的拓展:
不同电梯服务楼层区间不一致:
新增了以下类:
ElevatorConfig
:电梯属性类,存放电梯的开关门、上下楼速度、以及最大承载量。
ElevatorConfigBuilder
:电梯属性建造类,相当于属性工厂,客户需要哪种类型的电梯属性,只需要询问该工厂,其就会返回对应的电梯属性。
上述两个类相当于工厂模式,在电梯初始化时只需要通过ElevatorConfigBuilder
即可获得所需要的电梯属性。拓展性也较好,只需要在该类中增加需要新增的电梯属性即可使用。
最大承载量:
修改了部分逻辑:
只需要在电梯进人与放人过程维护该量,当该量大于最大承载量时禁止捎带即可。
换乘功能:
新增了以下类:
RequestSp
:特殊请求类,该类继承自PersonRequest
类,增加了诸如换乘层,第一次电梯和第二次电梯等属性;为换乘功能服务。SchedulerController
:调度控制类。该类服务于调度器Scheduler
,内部包含解析请求的方法,作用是对于输入的每个请求,为其安排合适的电梯。修改了部分逻辑:
对于请求:之前作业的请求单部电梯执行一次便可执行完毕,而本次作业由于换乘功能的存在,需要对请求进一步细化:即无需换乘请求、第一阶段请求、第二阶段请求。
对于执行:调度器在解析请求时即可分析出该请求是上述哪种请求,并做相应操作:
由上述逻辑可以知道,RequestSp
类需要支持各种请求的变种,来匹配功能。
多电梯调度优化:
ElevatorData
:电梯信息类,包含诸如电梯当前层,电梯目标起始、目的层,电梯方向等电梯属性,主要是服务于多电梯调度算法。但由于个人原因并未实现动态的调度优化。1.2 优缺点分析
PersonRequest
类和多态运用较为简便地实现,体现了OOP的特性与妙用。1.3 设计原则检查
PersonRequest
类进行了继承,并在父类出现的地方使用了子类代替,满足里氏替换原则。? (度量符号说明参见注记,下面对个别新增度量进行注释)
? LOC
:Lines of Code: 类/方法代码规模
? NAAC
:Number of Attributes: 类属性个数
? NOAC
:Number of Methods:类方法个数
? CONTROL
:Number of Control statements:控制分支个数
代码统计
类度量
类复杂度度量
方法复杂度度量
可以看到复杂度仍然集中在Elevator
和Scheduler
类中,代码逻辑仍然需要进一步细化。不仅如此,抽出的静态类方法ElevatorController
和SchedulerController
虽然在逻辑上简化的电梯类和调度器类,但在数据上似乎反而增加了复杂度,这与静态类的计算有关,但我认为这样的做法确实简化了逻辑复杂度。
? 在中测和强测均通过正确性,但是在性能方面没有获得较高收益。
? 第七次作业是电梯系列作业的终点,在各方面均对OOP程序设计能力进行了较高的要求,这里引用助教的话详细说明:
基础部分,那么需要做的是设计好线程安全的架构,然后把工作流的先后顺序处理好
- 具体来说,工作流处理指的是指令的拆分,比如对于指令2-->3,如果拆为2--B->1和1--C->3的话,那么毫无疑问,1--C->3必须在2--B->1之后执行,才符合逻辑顺序。
- 本次基础部分的架构设计所需要处理的问题就是这些- 进一步要求,那么则需要继续像上次一样,使用各类优化算法(SCAN、LOOK、ALS等),并且将这些优化算法进行最大限度的逻辑抽象和逻辑封装,做成统一建模、可配置、零耦合、可独立运行的三条电梯线程,并行工作。
极限性能,则各自完全独立的调度算法就不一定好使了。正如楼上部分同学所说,需要的是多电梯协同配合。就好比打游戏团战时,三个人默契的配合效果必然强于各自为战。这样一来,对调度器架构的设计会提出更高的要求——需要能进行跨线程之间的协同,并基于不同线程的实时状况进行优化。或者说,所设计的架构必须要能充分支持跨线程的实时优化,你才有足够的潜力在不翻车的情况下进行极限优化。当然,除此以外,则需要的是一些脑洞了,甚至完全不必拘泥于传统的调度算法,可以自由发挥想象。
? 总而言之,架构设计是基础,单体算法与低耦合是进阶要求,多模块协同与交互是较高要求,三者相辅相成,在后续作业中需要进一步加深理解与强化练习。
设计原则--总原则:开闭原则
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码。即是为了使程序的扩展性好,易于维护和升级。
LOC
:Lines of Code: 类/方法代码规模NAAC
:Number of Attributes: 类属性个NOAC
:Number of Methods:类方法个数CONTROL
:Number of Control statements:控制分支个数标签:集合 9.png 不同 特点 nts 疑问 限制 系统 分析
原文地址:https://www.cnblogs.com/lsj2408/p/10744063.html