oo第二次总结
一、多线程的学习
1.多线程概述
多线程,我理解为同时有多个程序代码段在抢占cpu的运行,而cpu时间的具体分配,则是按照其优先级随机分配。也就是说,多线程之间具体的运行顺序,程序员是无法决定的。从操作系统的角度来说,多线程使得电脑设备资源的利用率提升了。
而在本次课程中,多线程的作用则主要是用来模拟多个现实中的物体的并行处理事件。个人认为对于这种情况来说,单线程的效率是更高的。但是多线程更加直观,易于理解,且对于后续的代码更新和维护更加友好。
2.多线程运用
由于这3次作业的逻辑都还并非十分复杂,每个线程的睡眠时间不需要依赖于其他线程,因此此次的作业只需要用到sleep来实现阻塞。
为了代码的可拓展性,本人在这3次作业中使用了Runnable接口并利用有Runnable接口的类来创建多线程。
二、个人作业分析
1.第5次作业(多线程电梯)
1.度量分析
在本次作业中,通过度量分析,可以发现Rutile类中的run函数逻辑过于复杂,不利于后续的代码修改和维护。
Rutile类其实就是调度器,由于本人将电梯的选择和判断很多都封装在了run函数里,所以导致run函数过于臃肿,以后需要避免。
2.类图
其中Bort是主类,其中包含了主函数。Benitoite类是输出类,方便调试,且保证输出的线程安全。
Jade类用来读入和给需求类Cinnabar赋值,而Obsidian类则专门用来检查格式。
Rutile类是调度器,调度器获取需求后将需求放入等待队列Antarcticite类,当出现可以满足请求的电梯时将其放入那个电梯的队列Beryl类中。
Euclause是电梯类。
3.时序图
4.设计缺陷
首先,调度器的run函数过于臃肿,其实可以对于每种捎带或空闲都分理出一个函数来判断,应满足方法功能尽量单一的原则。
其次,类别的命名不好,不能顾名思义(其实是因为我个人喜欢宝石之国)
函数功能的划分不够合理和细致。
5.bug分析
本次作业主要存在两个bug:
(1)对于同一时刻的电梯请求,先输入的请求会对后面的请求产生影响,这一点在我的程序中没有体现。
(2)没有采用生产者-消费者的输入模式而是自己对input加锁,代码如下:
输入部分:
for(;times<=50 && end==false;++times) { read_new = false; str = Alexandrite.nextLine(); judge = new Obsidian(str); synchronized(read_new) { num = judge.process(req); read_new = true; if(times==50) { end = true; } //while(read_new && end==false); } }
调度器部分:
while(true) { if(input.getNew()==true) { synchronized(input.read_new) { for(int Morganite=1;Morganite<=input.getNum();++Morganite) { wait.add(req[Morganite]); //Benitoite.print("add to wait:"+req[Morganite].toString()); } input.useNew(); } } .........
那么可能出现一下情况,调度器类拿到了锁,此时输入类刚刚处理完上一个输入,还没拿到锁。那么此时如果接着输入的话,这一行就有可能被输入类所忽略。这是一个很严重的bug,这提醒了我以后要更加严格地测试产品功能。
6.互测bug分析
感觉我这次测试的人很多功能都没有实现呢。。谈不上找bug了。。
7.心得体会
任何程序,都一定要彻底想好了,把需要的功能分解到极其细微的地步,才应该开始写。
1.第6次作业(文件系统)
1.度量分析
这次的作业,代码逻辑较为复杂的地方是对于处理方案的检测函数,个人实在是不知道这种本就应该充斥着条件语句的函数如何化简。
2.类图
Eos表示输入,Nyx表示输出。
Hephaestus类是主类。
Vulcanus类是文件操作的父类。期限面衍生出4个子类。
Euehenia表示检测大小变化。
Thalia表示检测路径变化。
Eucleia表示修改时间变化。
Eupheme表示名称变化。
3.时序图
4.设计缺陷
变量命名不能顾名思义
5.bug分析
此次的问题主要在于当监控目录时,该程序不能判断同时有多个文件修改名称或路径的情况。
这次的指导书改来改去,太迷了,最后也弄不清它的要求。。
6.互测bug分析
这次遇到的人根本不支持目录操作。。
7.心得体会
感觉这一次的作业比较迷。。我觉得真正的文件监控不应该是这样的。。毕竟这样的判断会有一些漏洞。。
不过这次的作业代码模式比起上一次进步不小,将四种监控的重复功能放在父类中减小了代码的冗余,同时易于调试和修改。此外此次代码的函数功能也都尽量简单了,没有再出现上次作业那样的100多行的接口。
此外也发现了自己对于整体代码架构的把握能力不足,代码还是不够简洁优美,一些细节方面多加了不少细枝末节的判断,如果整体架构好的话是不需要那么多判断的。
3.出租车
1.度量分析
这次的代码还是犯了调度器类run方法过于冗长的错误,毕竟这是一个很难避免的错误,当一个方法需要满足复杂需求的时候,就很容易把它扩充得过胖。
2.类图
ouput类表示输出,input类表示输入。
general静态类用来存放一些常用的小函数。
city类存放地图和向导。
creator类是主类。
request类是乘客请求。
调度器接受乘客请求并在时间窗口内不断询问所有出租车,看哪些满足条件,然后分配请求给出租车,接下来的事情都在出租车线程内部自己解决。
3.时序图
4.设计缺陷
调度器类的run函数还是过于臃肿。
代码可扩展性不强,很多函数的功能性还是不够单一。
5.bug分析
每次停车是没有停留1s。。阅读指导书不够仔细。。
6.互测bug分析
这次的被测者输出没有锁。
而且对于出租车在三秒时间窗口内到过课装载的范围内但之后又出去的这种情况判断失误。
7.心得体会
这次作业算是这三次作业中比较简单的一次,整体逻辑没有太过复杂,就这么顺风顺水的写完了。。
三、总结
总的来说,关于线程安全方面,个人的感觉是尽量避免锁代码块,因为这样会陷入复杂的逻辑思考。理想的多线程程序应当是这样的:每个线程之间互不干涉,涉及到线程交互的类中的会改变类自身属性的方法上加锁。这样应该就可以处理大部分情况了。
而关于如何查找他人bug,我都是先把代码看一遍,逻辑理一遍,然后肉眼查看出可能有漏洞的地方构造几组数据测试一下。。