BUAA OO面向对象第二单元总结

同步块与锁

第一次作业中,等待队列是一个被共享的数据,每种等待队列都提供了写入和读出的方法,这些方法都是用synchronized修饰的,锁的对象为队列本身,因此其他类对等待队列进行操作时,是线程安全的。inputthread类负责写入需求,若输入结束,则设置结束标志,避免轮询。电梯类调用有读出方法,若等待队列为空,使用wait notify方法进行等待,若有请求进入队列或输入结束,则释放锁并唤醒等待的进程。

第二、三次作业与第一次作业类似,不过设有两级等待队列,锁的对象依然为各级等待队列。输入线程和总调度器负责总等待队列的写入和读出,总调度器和各个电梯负责各个等待队列的写入和读出。

在进行作业之前根据课上实验内容学习了生产者-消费者模式,我所设置的锁都与数据输入输出密切相关,因此并没有出现数据冲突,死锁等问题。

调度器

本次作业使用了两级调度器,在第一次作业中,只有电梯调度器,因为只有一个电梯,所以调度器实际上是电梯类内部的方法,根据不同的输入模式,有不同的调度方法。random模式下,调度器从总等待队列中获取请求,起始位置距离电梯最近的请求优先级最高,在电梯运行过程中,遍历等待队列,判断能否捎带。morning模式下,电梯会在一层等待一段时间,night模式下,电梯会由高到底捎带,这一可捎带的电梯调度器一直用到了第三次作业。

第二、三次作业中,设置总调度器,输入线程将所有请求放入总等待队列,总调度器将请求按照调度规则分配给不同电梯的等待队列,总调度器同时与电梯的状态进行交互,电梯所处楼层、方向、人数都是调度算法的基础。所有调度器和电梯通过队列提供的读取写入方法,与等待队列进行交互,保证线程是安全的。其中,第二次作业总调度器算法较为简单,主要是将乘客平均的分配给各个电梯。第三次作业中,总调度器的作用更为重要,但是我在写换乘算法时出现负优化的情况,因此放弃了一般性的换乘策略,转而判断几种特殊情况,这种算法显然不符合实际需求,需要多加注意。

架构分析

UML类图

总时序图

电梯类时序图

作业整体上使用生产者消费者模式,以:总等待队列->总调度器->各电梯等待序列->各电梯 的顺序来生产和消费。

从功能上分析,由于我将电梯调度器整合到电梯类中,电梯类显得比较臃肿,在三次作业中,电梯种类的区别仅仅体现在运行时间、人数等电梯的固有属性,因此通过继承一个电梯类,来实现不同功能是可行的。但如果要拓展不同的电梯运行策略,没用单独的电梯调度器的弊端就体现了出来,对于不同的电梯运行方式,我需要几乎重写整个电梯类,而如果有电梯调度器类,只需改写电梯调度器即可。对于总调度器,它负责总等待序列,各电梯等待序列,换乘等待序列的分配工作,在二、三次作业中作用显得更为重要,如果要拓展新的调度策略,只需改进总调度器算法即可。

从性能上分析,由于整合入电梯类的电梯调度器不易改写,因此从第一次作业一直沿用到最后,也逐渐感受到了运行方法的不足,因此性能并不是很好。对于总调度器,我的方法属于特判类型,缺乏一般性,扩展性不好。

分析自己程序的bug

在进行第一次作业之前,学习了生产者消费者模式,因此未出现死锁、数据冲突等问题。

在第一次作业中,由于考虑不周到,在换乘时会固定开关门一次,在运行过程中出现了电梯到达一层开关门一次,进行换乘开关门一次,两次开关门的错误。在morning模式中,在第一次等待时间的计算出现问题,导致tle。

在第三次作业中,对换乘考虑不周全,被特殊数据hack,导致超时。

发现bug方法

对于一般性bug,主要是电梯运行过程中开关门,上下电梯的错误,可以用特殊数据查找,比如只有一个人进入时。

对于多线程出现的bug,如死锁等,一方面要读懂别人代码,了解他的进程同步模式,一方面可以用自动测评机,运行大量数据,寻找死锁的情况。

心得体会

在进行作业前,学习相关设计模式,对于架构设计有好处,能够有效减少线程同步出现的问题。在编写多线程代码过程中,要时刻注意锁的使用与释放,注意共享的数据。多线程程序的调试与一般程序不同,我还需要多加学习。

posted @ 2021-04-27 18:09  apakiser  阅读(65)  评论(0)    收藏  举报