OO第二单元总结

第二单元总结

第一次作业

设计思路

主要通过两个线程(不包括main类)实现电梯运行,Provider线程实现读取输入,Elevator线程实现电梯的运行,并有一个策略类Strategy实现电梯运行去往楼层的策略。Morning模式简单采用先停留2s,对候乘队列按目的楼层排序,再运行;Nignt模式便是对候乘队列按到达楼层排序,再运行;Random则直接按策略类的ALS策略运行。

同步块与锁

本次作业使用的是synchronized + 代码块的方式进行同步控制。而这次作业只有一个共享对象WaitPeople即等待乘客的队列。所以只需要在输入线程以及电梯线程中,在进行相应的读写操作的时候,加上synchronized(WaitPeople){}即可,并在此语句块中加上WaitPeople.notifyAll唤醒另一个等待线程。

调度器设计

本次作业只有一个电梯,个人感觉不需要进行调度器的设计,到来一个乘客,便加进电梯即可,无需调度器分配。

bug分析

第一次作业的bug主要是由于自身对于ALS算法的捎带策略实现出现了部分问题,导致电梯在运行时只能捎带少量特殊乘客,最终强测许多点都超时了,最终进行捎带策略的修改,提升电梯整体运行效率,修复了bug。

 

第二次作业

设计思路

主要通过两个线程(不包括main类)实现电梯运行,Provider线程实现读取输入以及输入乘客的调度(称之为控制线程),Elevator线程实现电梯的运行,并有一个策略类Strategy实现电梯运行去往楼层的策略。Morning模式简单采用先停留2s,对候乘队列按目的楼层排序,再运行;Nignt模式便是对候乘队列按到达楼层排序,再运行;Random则直接按策略类的偏向LOOK算法的策略运行。

同步块与锁

本次作业仍使用的是synchronized + 代码块的方式进行同步控制。而这次作业有多个共享对象如waitPeopleList(i)即对应电梯等待乘客的队列、waitPeopleList即所有电梯的等待乘客序列列表、processPeopleList即所有电梯的运行乘客序列列表。

在控制线程中,当涉及对相应电梯分配候乘乘客时,即要对其对应的WaitPeople进行加锁;当加入电梯时,要对整个waitPeopleList以及processPeopleList进行加锁。其本质都是为了避免读写操作出现冲突矛盾,在关于相应的电梯等待序列,控制线程中要在相应同步块中加上唤醒操作,以唤醒相应的电梯线程。

调度器设计

本次作业由于有多个电梯,按道理是需要加调度器,但我采取的做法是把调度器的功能融入到输入线程中,从而整体还是两个线程,因为个人感觉如果因为调度器而加一个线程,可能会导致更多的线程安全问题;但这样的方式也有缺点,在乘客输入后,即马上把他分配完成,可能此分配策略不是最佳,后续需要对多部电梯中的人进行调整,这样也会产生许多问题。个人bug也便出在此。

调度器的设计思路主要是分布式调度,按照平均原理进行分配,以保证每部电梯都在尽量运行,不至于某部电梯负载过大。而当输入请求结束时,所有乘客也分配完成,对所有电梯置end = True,即可完成电梯线程的结束。

bug分析

第二次作业出现的bug主要问题在于不同电梯的等待列表之间乘客的流动出现问题,比如A电梯原本主请求是a乘客,但由于候乘人员变动,a乘客转到了B电梯,而导致A电梯出现运行问题。此问题的出现主要在两点,一是部分同步块未加锁,导致本不能对候乘列表操作时进行了操作,出现线程安全问题,导致CPU运行超时;二是调度时,将候乘人员较多的电梯里的人转至较少人的电梯时,转移策略有问题。最终对部分同步块加锁以及调整转移调度策略,修复了bug。

 

第三次作业

设计思路

主要通过两个线程(不包括main类)实现电梯运行,Provider线程实现读取输入以及输入乘客的调度(称之为控制线程),Elevator线程实现电梯的运行,并有一个策略类Strategy实现电梯运行去往楼层的策略。并在控制线程中实现了部分换乘策略。Morning模式简单采用先停留2s,对候乘队列按目的楼层排序,再运行;Nignt模式便是对候乘队列按到达楼层排序,再运行;Random则直接按策略类的偏向LOOK算法的策略运行。

本次作业基本是按照上次作业的结构进行迭代开发,所以同步块与锁调度器方面大致结构与上次一致。

同步块与锁

本次作业仍使用的是synchronized + 代码块的方式进行同步控制。而这次作业有多个共享对象如waitPeopleList(i)即对应电梯等待乘客的队列、waitPeopleList即所有电梯的等待乘客序列列表、processPeopleList即所有电梯的运行乘客序列列表、allWaitPeople即所有的总的候乘乘客队列。

在控制线程中,当涉及对相应电梯分配候乘乘客时,即要对其对应的WaitPeople进行加锁;当加入电梯时,要对整个waitPeopleList以及processPeopleList进行加锁;当相应电梯实现完换乘请求后,便需要新抛出一个请求加入到allWaitPeople中,此时便要对allWaitPeople进行加锁,并在同步块中加入对控制线程的唤醒操作,而只有当控制线程在等待时才实行唤醒操作。这些加锁操作的本质都是为了避免读写操作出现冲突矛盾,在关于相应的电梯等待序列,控制线程中要在相应同步块中加上唤醒操作,以唤醒相应的电梯线程。

调度器设计

调度器的设计思路主要是分布式调度,按照相应比例进行分配,即A电梯可以运8人,而C电梯只能运4人,在分配乘客时,便乘以相应的系数,但这样的考虑确实是片面的,有点想当然。此法主要目的还是保证每部电梯都在尽量运行,不至于某部电梯负载过大。

至于换乘,我主要考虑两种情况,一种是乘客是从奇数楼到偶数楼,便用B电梯把其送到最靠近目的地的奇数楼,然后用A电梯或C电梯完成之后操作;另一种是C电梯处理1-3楼或是18-20楼的情况,剩余交给A电梯或B电梯。这样的换乘策略仔细一想,其实并不一定能够提升最后的性能,多次的开关门也耗费了大量时间,加入此换乘策略后,还引起大量线程安全问题,确实是得不偿失。

而当输入请求结束时,所有乘客也分配完成,但由于有换乘的存在,不能直接结束某些电梯,需要判断所有电梯候乘队列以及运行队列中是否有上述两种换乘情况,如果有则需要wait,直至所有电梯中都没有换乘的请求,之后对所有电梯置end = True,即可完成电梯线程的结束。

UML类图

UML协作图

可拓展性

方法分析

类分析

由上述方法以及类的分析可知,部分类如Elevator以及Provider两个主要的线程复杂度较高。

此次作业完成的设计,从功能方面来看,较为完善的完成了需求,即可以实现多部不同型号电梯的运行,性能方面,却是不尽如人意,换乘策略的考虑,难以验证是否达到了加速的效果。

如果考虑程序的可拓展性,由于我未将电梯参数作为变量,所以当加入新型号的电梯,程序需要修改的地方还是比较多。可能使用工厂模式,对于加入电梯,应当会更适合一点。至于电梯停靠楼层,如果不考虑换乘,那么加入新电梯,只需要对应分配即可;考虑换乘的情况的话,那么就要考虑新加入的电梯与原本已有型号电梯之间的交互,可能较难处理,此方面来说,可拓展性较差。

至于乘客的不同到达模式,应该还是比较好修改的,只需要在策略类中新加入对相应模式的处理即可。

bug分析

第三次作业出现的bug主要在调度线程唤醒问题,当调度线程状态是WAITING或者TIMED_WAITING时,都要及时唤醒,之前只是当其时WAITING时才唤醒,导致最终出现一直等待的情况,以致超时。最终对唤醒条件判断加入状态TIMED_WAITING,并进行适当修改,修复了bug。

测试bug策略

在自测中,个人主要是随机自我构造边界数据,如从19、20层到1楼的情况,或是采用已有强测数据进行测试,一般测出的问题都是线程安全方面的问题如死锁或是一直wait没有唤醒。基本上都能通过调试或是使用Jprofiler解决。

互测中,第一次互测,房间内其他人大多数bug在于不能正确接到乘客或是乘客无法正常到达,我是使用自测中采用的边界数据进行测试,所以此类bug较容易测出;第二次加入多个电梯,除去第一次互测的部分数据,还进行了在最后时刻加入电梯的数据测试,以测试加入电梯是否有效,测出部分人除了乘客到达问题以外,还具有线程安全类问题。第三次由于加入不同型号的电梯,则可构造针对某种算法的数据,如不换乘的算法,构造只能一个电梯跑的数据,如全是偶数层到偶数层的数据。

本单元的测试思路与上一个单元相差较大,首先由于多线程自身的不确定性,测试出现的bug较难复现,定时输入测试也具有一定难度,结果的正确性检查也耗时较长,所以测试方面较为麻烦,相比第一单元,更需要大量的测试,才可尽量保证不会出现线程安全问题。

心得体会

本单元主要是关于多线程方面的训练,其中线程安全是大多数bug的原因。理论课上讲过的线程安全类隐患基本上都可能会在作业中出现,而我个人遇到的比较大的问题主要是关于共享变量的读写操作加锁与否,有时写着便忘记其是共享变量,而忘了加锁。这类问题,在第一次作业中尤为常见,一开始都不太清楚原因,根本上还是对线程安全理解不够全面;之后的作业,就轻车熟路了,基本上bug都能较快的定位。

至于本单元设计的层次化,个人认为并不是特别理想,由于个人把调度器融入了输入线程,导致此线程功能较为复杂;且电梯线程中,未完美的贯彻电梯就只让它运行的思想,策略类中的停靠策略不够完善,导致整体其实比较冗杂,这由第三次作业的方法以及类的度量分析可以看出。

整体来说,通过这一单元的学习,个人从一开始的对于多线程完全不懂,到现在已经可以对多线程进行相关实现,也可以基本保障线程的安全,这一点来说,我个人认为收获还是蛮大的,整体完成度还不错,但架构方面仍需提升,仍需多加训练努力。

 

posted @ 2021-04-25 11:28  枭丶  阅读(48)  评论(1编辑  收藏  举报