第五次作业:
设计策略:本次作业一共构造了5个线程,包括3台电梯,1个调度器和1个请求模拟器。请求模拟器从输入中解析请求,并加入到队列中,调度器从队列中获取请求,根据调度策略将请求加入到电梯的队列中,电梯从自己的队列中取出请求并运行。由于队列是线程共享的资源,所以队列的所有方法都加了synchronized关键字来保证线程安全。
度量分析程序结构:分析图如下所示。其中Elevator类的复杂度较高,其原因是电梯会进行部分捎带、同质和同层的判断。除此之外,调度器的分配策略比较复杂,考虑了很多因素,因此思路不够清晰,代码写得比较臃肿。设计原则存在缺陷:局部化原则。
自己程序的BUG:在输入大量请求的情况下,可能会出现输入END后不能结束程序的情况,原因是电梯状态没有及时更新导致队列里还有请求,不能正确地结束程序。
发现别人的BUG策略:本次作业抽到了一个公测不能实现正常功能的无效作业。
第六次作业:
设计策略:本次作业是一个请求对应一个线程,在用户输入完所有请求后,对请求进行解析,并构造相应的监控线程。本次作业比较难的部分就是判断文件是否触发条件。使用HashMap存储文件的snapshot,每扫描一次就根据snapshot判断文件是否触发条件,这比之前用ArrayList作为snapshot容易得多。本次作业中线程共享的资源是输出流和文件,因此PrintStream类的方法都加了synchronized关键字来保证线程安全,文件也使用了线程安全的文件类。
度量分析程序结构:分析图如下所示。Monitor类的复杂度很高,主要原因是本次作业的大部分任务都由这个类来完成,因此Monitor类也成为了一个God类。
自己程序的BUG:最多只能监控9个监控对象(要求是10个),其原因是把判断监控对象数量的语句中的”<=10”错写为”<10”,就产生了这个BUG。
发现别人BUG的策略:本次作业中分类树的一些结点其实可以归结为一个BUG,根据分类树构造了一些简单的测试样例进行基本测试。测试了之后简略地看了一下被测试者的代码,发现了删除目录时递归的方法错误,然后在测试线程中构造了相应的测试语句,产生了Crash错误。
第七次作业:
设计策略: 本次作业的设计策略与多线程电梯比较相似,但是个人觉得难度上比多线程电梯简单一些。本次作业一共构造了102个线程,分别是100个出租车线程,1个调度器线程和1个请求模拟器线程。请求模拟器从输入中解析请求,并加入到队列中。调度器扫描队列,根据时间、出租车状态和请求状态进行请求的处理。出租车根据队列分配的请求进行移动,没有请求响应时则随机选择一个方向移动。由于队列和出租车状态是线程共享的资源,所以队列的所有方法以及出租车的部分方法都加了synchronized关键字来保证线程安全。
度量分析程序结构:相关的图如下。本次作业中各个类的分工比较明确,实现了责任分配均衡原则。但是100个出租车对应100个线程,可能会出现信息错位的问题。
自己程序的BUG: 没有考虑到起点和终点相同的请求要忽略,因此产生了一个BUG。Request类中一个函数返回值大意地写错了,进行基本测试的时候也没有发现,因此又产生了一个BUG。
发现别人BUG的策略:本次作业测试较为困难,因为出租车的初始位置是随机的。所以主要通过看GUI和文件输出来判断是否产生BUG。首先根据错误分类树来构造一些简单样例进行测试,之后构造上千条请求并同时输入进行压力测试。先根据GUI找到可能存在的BUG(GUI只是一个参考),之后通过文件输出的内容确认了BUG的存在。
心得体会:
这三次作业比前几次都复杂,在每一次做作业的过程中都会遇到困难,但还是咬牙坚持了下去。特别是多线程电梯,第一次开始接触多线程这个概念,不知道多线程应该怎么写,在构思程序时也毫无头绪,后来经过百度和询问同学后才开始写代码。写完程序之后,在进行测试时又发现了线程同步方面的问题,让我十分头痛。于是又进入到了无尽的debug过程中。但是通过这一次作业,让我对多线程和线程安全基本有了一个了解。
在做之后的作业时,我明显感觉到自己的能力提升了,比之前更熟练了。虽然这两次作业也是比较复杂的,但是我完成作业的速度比之前快了不少。这三次多线程作业给我的感受就是,线程安全是第一位的,一定要保证线程安全,否则会出现奇怪的错误。
除了写作业方面存在困难,测试方面也很花费时间。特别是第七次作业,个人感觉应该是这三次作业中最简单的,但是测试起来特别麻烦。因为多线程的随机性、出租车初始位置和运动方向的随机性,想要定位一个BUG比较困难。如果被测试者的GUI和输出部分没有做好,这就更难测试了。
总的来说,这三次作业让我付出了很多,也让我学到了很多,比如说对面向对象的理解更深了,对多线程程序的熟练度也提升了很多。