OO第五、六、七次作业总结

一、线程协同和同步控制策略

第五次作业

       本次作业中线程有上图中三类,分别为扫描线程、调度器线程、电梯线程,Scanner和Scheduler之间共用请求队列,Scheduler与三个电梯之间共用三个电梯的子队列和灯对象。所以线程同步的实现就是从共用对象入手,当调度器发现队列为空的时候wait否则一直扫描队列,Scanner每扫描到一个有效请求将其加入队列并notify,这样两者之间通过公用队列实现同步。调度器和电梯之间,公用灯对象和三个电梯的子队列,但是在实现的时候我选择了给电梯加锁,调度器获得电梯的锁之后才可以把请求加入到电梯的子队列,电梯释放自己的锁的条件是电梯处于运行状态(电梯每上一层楼执行this.wait(3000)),除此之外获取灯对象的状态时(判断同质时)和电梯每到达一个楼层要熄灭相应的灯时首先要获得灯的锁。

第六次作业

扫描二维码关注公众号,回复: 97278 查看本文章

       本次作业的具体实现思路是,首先一次性把监控任务读取完毕,过滤掉重复的、无效的任务指令后每个任务开启一个监视线程,线程start的时候首先扫描监控对象的所有文件,建立对应的HashMap作为每个监视线程的私有属性,HashMap的Key为文件的绝对路径,Value为一个数据封装对象,包含文件名、大小、最后修改时间,然后while true扫描监控范围将每次的扫描结果与旧的HashMap做对比,检查是否满足触发条件,然后执行相应的任务。本次作业的线程安全主要在于Detail与Summary两个数据存储对象,在本次作业中我实现线程同步的策略是对Detail和Summary两个数据记录对象加锁,当监视器满足触发条件并且任务是RecordDetail或者RecordSummary的时候,当监视器向Detail或者Summary记录数据时需要先获得Detail或者Summary的锁,除此之外,Detail或Summary在定期向文件输出记录内容时也要先获得自己的锁,如此以来可以实现线程同步与数据安全。

第七次作业

       本次作业的实现思路是首先构建一个静态Map类包含地图信息,以及BFS静态方法用于寻找最短路径,程序开始运行时首先要将Map初始化,然后开启主要的线程,包括指令扫描线程,出租车管理线程,以及针对每个具体指令开启为期3s的监视线程,扫描线程与Controller共用一个指令队列,当指令队列为空时Controller进行wait,每当Scanner扫码秒到一个有效的指令就执行notify方法,二者间的线程同步较为基础简单,Controller为每个请求开启一个为期3s的监视线程,当3s监视时间结束时Monitor把相应的请求加入到对应的出租车中这个时候需要获得对应出租车的锁。关于出租车的运行模式实现,我没有把每个出租车设计成线程而是设计成一个类似于有限状态机的对象,状态的改变来自租车管理线程的刷新信号(类比计组实验中的时间上升沿),出租车管理线程只负责每0.2s向出租车队列中的100个出租车提供刷新信号,关于出租车状态的更新参看下图。可以修改出租车的数据的只有Monitor对象(也就是派单这一行为),由于派单(条件是目标出租车处于WFS状态而且为最优出租车)时执行的方法会立即修改对应出租车的状态为GFS,将此方法置于同步块中就可以实现线程同步而避免不同监视器同时向一个出租车派单的现象发生。

 

二、程序结构分析

第五次作业

Metrics度量结果:

类图:

 

 程序分析:本次作业采取这种设计方式优点是思路较为简单明晰,采取上述的同步控制方式保证了电梯自己的一次调度(判断捎带以及下一条主指令等操作)绝对不会受到外界的任何干扰,缺点是同步块中代码量很大运行时间长了之后如果不采用模拟时间会导致时间误差越积越大,由于本次作业很大一部分设计思想和实现方法继承于前两次电梯作业,所以对于线程同步的实现一定程度上受制于此。另外代码风格方面做的还是有点差,代码的嵌套深度太大,圈复杂度也很高。设计原则方面,主要问题还是在DIP原则方面,代码面向细节的地方太多不能做到足够的抽象,重用原则方面做的比较差。

第六次作业

Metrics度量结果:

 

类图:

程序分析:对于本次作业的具体实现上,优点是程序的控制结构和作业流程清晰明确,线程同歩方面作业比前一次作业要好很多,未发现任何延时过高、死锁的现象,另外在本次作业中采用HashMap管理文件目录使对比查找变得方便很多。关于缺点,在线程同步方面暂未发现问题,主要问题还是存在于设计原则方面,代码面向Details问题严重,不能抽取共性然后上升到抽象层次,虽无关正确性但是这种设计习惯会导致代码的可读性,可维护性变得很低,在以后的作业中还是要认真对待这个缺点加以改进,对于增量式的作业而言这样的缺点可能会被慢慢放大最后影响正确性,多线程电梯作业中就出现了这个问题。

第七次作业

Metrics度量结果:

类图:

程序分析:本次作业中我采用了1*100的设计而不是100*1(具体参见第一部分),这样做的优点是出租车的刷新动作几乎是同时的在GUI上显现的比较明显,几乎无延时出租车位置同时变更,在线程安全方面也减少了很多问题几乎不存在线程安全隐患,缺点是可能这样做可能有点违背了模拟的本意,有一点取巧的意味。在本次的Metrics度量结果中可以看到,抽象问题相较于前两次作业有所改善,但是嵌套深度问题依然存在,在这一点还需改进。设计原则方面,本次作业被报了一个懂我原则方面的缺陷,在类、属性的命名方面,做的确实不够好后续作业中要做到顾名思义,少偷懒。

三、BUG分析

第五次作业

       本次作业被发现一个bug,这个bug来源于线程同步做的不好,加了太多的synchronized锁导致时间误差越积越大,最后导致正确性的问题。究其原因是在电梯对象上加锁的选择导致同步块中包含进了大量的无需同步的代码,导致其他线程等待时间变长,误差越积越大影响正确性。

第六次作业

       本次作业被发现一个bug,这个bug是公测bug,具体是监控目录的时候对Renamed文件进行Recover操作时又会触发自己的Renamed触发器,导致文件名在两个名字之间跳来跳去。究其原因,是执行Recover的时候忘记再一次更新快照,导致快照存储的是未Recover之前的结果这样下一次扫描又会触发,又会把文件名改回去如此反复。

第七次作业

        本次作业被发现一个bug,bug产生的原因是当出租车接单时正好位于乘客处此时BFS的结果为空,但是出租车运行时是在一个路径队列中每次取队头元素作为下一个位置的,又因为此时队列为空,就导致取队头元素时产生了Crash错误。

四、寻找bug的策略

 

1、结合bug树分点测试寻找bug

2、阅读代码,寻找逻辑漏洞或者存在线程安全隐患的地方

3、由于多线程程序具有随机性,有的问题在一次测试中可能体现不出来或者不明显,故可以使用较多较大的测试数据随机轰炸

4、构造测试数据对边界情况进行测试

五、心得与体会

 

1、为了保证正确性而一味地增加同步部分实质上是把bug转移到了别的地方,回头看自己加锁同步的部分发现很多是不必要的,冗余的同步操作最终会体现在正确性上,在保证安全的前提下应该尽量减少同步代码块,提高性能

2、多线程程序的bug更难发现,要使用更大量的测试数据去监测自己程序的正确性,寻找隐藏的不易被发现的bug,而不是测了一两组数据发现结果正确就认为程序没有问题

3、减少代码中重复的部分,把共用代码抽象对简化代码、提高可读性、debug有很大的帮助,漂亮的代码一定是简洁明了的而不是很深的嵌套,大段的重复

4、类和属性的命名不能偷懒只是自己明白就可以,而是要尽量做到顾名思义,自己写的代码将来很多情况下不是只有自己一个人使用的,好的命名风格会给程序加分

5、完成这三次作业后,发现设计原则中最难实现的还是DIP原则,反观这几次的设计很大一部分实现还是面向Details这也是Metrics度量中圈复杂度和嵌套深度过高的主要原因,在以后的作业中还是要大力改进自己这方面的问题

猜你喜欢

转载自www.cnblogs.com/CCrain/p/8978355.html