多线程初体验

多线程初体验

​ 第二阶段的这三次作业,自己写的还是蛮惨的,找不出来别人bug,但一直在被别人挂bug,不过这三次作业遇到的互测者态度是真的友善,2333。恩,下面逐一分析一下这三次作业代码。


第五次作业——多线程电梯

​ 本次作业有实际意义的类共有5个。当然还有其他一些辅助的enum,或者单纯传递数据用的类,都比较简单。回忆一下,这次作业写得挺狼狈的,还是因为最初的设计考虑不周造成的。最初,想到这次的电梯是实时处理请求的,所以可以来一条请求就处理一条请求,所以采取了把接受输入线程与分配请求线程放到了同一个线程中的方式。这个设计方案已经完成之后,才意识到无法处理那些后输入但是因为捎带而可以先响应的请求。这是一个挺不应该的失误,因为捎带是这几次电梯一直强调的问题。这个设计失误,导致后续又不得不进行了较大的修改。

​ 这次作业和周围的同学设计方案之间差别挺大的。因为比较喜欢三个电梯各扫门前雪的感觉,所以采取了电梯中设置内部类,就是类图中带有“internal”的SelfManager。同时,SelfManager也继承了前几次作业的调度器,为了保持类图简明,就没有体现出来了。还有一个内部类就是BlockedRqManager,这个就是由于设计失误导致不得不后期硬加上的一个线程,专门用来处理捎带问题。

​ 从类图上看,Elevator和Request属性和方法最多,但其实都是一些简单的动作,代码量不大。主要的难点应该是在SelfManager以及RequestLoader上。SelfManager有三个线程,分别操作三台电梯,用于遍历请求队列并且选出属于自己的请求,指导电梯响应请求;这一部分是120行左右的代码,其实也不是很多。RequestLoader是接受输入的请求,并且根据当前是否有电梯可以响应请求而决定将其加入请求对列还是加入阻塞队列。由于后来加入的阻塞队列的概念,所以不得不加入BlockedRqManager线程,专门用于管理阻塞队列。

​ 这次作业觉得设计失误比较大,但是最终bug并不是出在设计方案上,而是输入输出的问题上。无效请求的输出格式漏掉了时间T,QAQ;输出的系统时间为了和电梯时间保持一致,而采取自己计算出的时间,而非直接输出当前系统时间。还有一个,是真的长见识的一个问题。因为考虑到一条输入要提取的信息量比较多,所以没用正则表达式,而是一块一块的判断顺带提取数据。问题就出在了String.split()方法上。

String str = "hello,,world,,,";
String[] jar = str.split(',');
for(String tmp :jar) {
    System.out.println("#"+tmp+"#");
}
// 我原本以为会这样输出
#hello#
##
#world#
##
##
##

// 实际是这样输出的
#hello#
##
#world#

我企图使用jar的length来判断结尾多余的符号,但是实际并不行,导致会将非法请求当作合法请求处理。emmm,对此表示,以后绝对老老实实写正则表达式。


第六次作业——文件监控系统

​ 分析了好久指导书,然后觉得由于种种限制,作业仿佛似乎没有那么难。但实际做起来问题蛮大的。

​ 对着类图说,设计思路还是很直白的。一个Monitor会持有一个触发器FlipFlop和一个动作Action;Monitor线程不断地通过调用FlipFlop的detect()方法,判断是否出现了相应的文件变化,如果出现了相应变化,就调用Action的act()方法。感觉这个思路很符合ifttt的模式。FlipFlop和Action都是抽象类,有各自具体的子类,分别实现了detect()和act()方法。至于ThreadSafeFile,考虑到进行文件操作会需要很多类锁,故提供了一个纯静态的类,只提供静态方法。

​ 这次的代码量从类图就能直白地看出来,FlipFlop承包了很多的代码量,也有一些代码是做单纯储存数据的类,没必要赘述了。这次其实也存在一点问题,因为最开始考虑到Modified与sized-changed可能需要继续监控重命名和路径移动后的文件,所以将findRenameTarget()与findPathTarget()这些方法放到了父类里面去实现。最后表明,这两个方法分别只有renamed和path-changed用到了,所以放到父类当中并不合适,增大了父类的体量。最后FlipFlop达到250行左右,其子类平均60行的样子。

​ 这次作业的bug纯粹是自己很迷了。因为快照保存了所有文件信息,删除或者移动、重命名的文件也会保存,这样导致了已经改变的文件在报告一次之后,会继续报告这个文件,测试者用“疯狂虚区”描述,贴切。明明是很明显的bug,也不知道为啥当时没测出来,可能就是当时重心都放到解决recover导致的线程不确定性问题上了吧。


第七次作业——出租车系统

​ 这次自己的设计又是感觉稍微有点鬼斧神工。因为感觉上一个请求要维持一个3s的抢单窗口,所以不如一个请求有它自己的一个线程,持续3s,这样就很方便的完成请求的派发了,这样写起来其实挺简洁的,就是挺怕线程太多导致什么未知问题的。

​ 也正是由于一个请求一个线程的写法,所以类之间比较均衡,原来要承担比较多任务的Manager分担了相当一部分给TaxiTask。

​ 这次作业遇到了一个多线程的坑,会把两个请求分配给不同的电梯,刚开始还以为是路径求错了,研究半天才发现,是同步互斥没处理好。后来想到了操作系统讲的,test lock and set,将其变成原子操作就可以避免这个问题了。

​ 这次感觉功能上没啥问题,就是又不仔细了,没有完成忽略出发点与请求点一致的请求。


​ 这一阶段的作业确实挺差的,可能是难度有所上升,也有自己热情有点下降的原因吧。嗯,下一个阶段还有待继续努力吧。尤其是设计方案上,还是要对设计方案先进行一下具体的可行性分析,再进行作业。

​ 记得写多线程电梯之前的那几天,对于即将写多线程这件事不甚惶恐。找了一本《Thinking In Java》匆忙地翻了几页,后来写的时候发现,和自己用到的东西完全不同,所以写了三次多线程作业之后,还是感觉自己对多线程这个概念以及java中的多线程知之甚少。嗯,这个也是要继续深入学习吧。

猜你喜欢

转载自www.cnblogs.com/VI3160846668/p/8976082.html