OO第二单元总结
一、同步块的设置和锁的选择
在本单元的三次作业中,我只使用了同步块(synchronized),来给需要对共享对象进行操作的方法或者代码块进行加锁,和wait、notifyall,来挂起和唤醒线程,而并未采用lock锁及其系列操作,原因是使用synchronized就可以很好地解决本单元作业共享数据安全问题,具有较好的性能,并且语法简单、固定,易于分析理解,不易出错。
在前两次作业中,共享对象只有第一级托盘(waitQueue)和第二级托盘(processingQueue),而这两个队列都是RequestQueue类的具体实例,所以只需把RequestQueue类中对实例内容进行改动的方法都用synchronized锁起来就可以了,此外,还有注意要把电梯类中personIn()方法中的代码用synchronized (processingQueue)
锁起来,因为此处电梯进人要进行processingQueue的乘客删除操作,如果不锁起来,可能出现一个人同时进入两部电梯的情况。
第三次作业由于引入了流水线架构思想,新增了RequestCounter类,新加入了共享对象COUNTER
(RequestCounter类的实例),所以除了前两次作业要锁的内容,还要再把RequestCounter类中对实例内容进行改动的方法都用synchronized锁起来。
二、调度器设计
第二单元三次作业我都采用了相同的调度器设计,参考了本单元第一次实验课的调度器设计,采用两级生产者消费者模式,第一级“生产者—消费者”的生产者线程是输入线程,乘客请求输入后进入第一级托盘(waitQueue),被调度器线程(Schedule) 取出。之后调度器根据相应的策略将其放入第二级托盘(processingQueue)中(楼座/楼层类),由电梯线程(第二级消费者)通过自由竞争的方式取出。
下面解释三次作业中分别采用什么策略将乘客放入指定的第二级托盘(processingQueue)中:
-
第一次作业五个楼座对应五个processingQueue,根据乘客的所在楼座来将其分配到相应的processingQueue中。
-
第二次作业五个楼座和10层楼分别对应十五个processingQueue,根据乘客是横向还是纵向移动将其放入对应的processingQueue中。
-
第三次作业依然和第二次作业一样有相应的十五个processingQueue,但我采用了动态分配,让乘客每走一段之后更改
fromBuilding
、toBuilding
、fromFloor
、toFloor
,每段根据第二次作业的调度规则将其分到对应的processingQueue中。
三、架构设计分析
本次作业实现了简单的两级生产者—消费者模型,输入线程作为第一级生产者,总请求队列(waitQueue)是第一级托盘,调度器同时是第一级消费者和第二级生产者,每个楼座一个队列共5个队列作为第二级托盘,电梯线程是第二级消费者,同一楼座的电梯采用自由竞争的方式获得乘客。
电梯运行策略我采用的是指导书上写的ALS策略,同时结合电梯内的人数以及电梯外乘客的乘坐方向综合判定是否需要中途携带乘客。
第二次作业分析
本次作业在上一次作业的基础上,因为增加了十个楼层的横向电梯,故增加了十个processingQueue。为横向电梯再写了一个策略类,其余架构与上一次作业相比未改变。
第三次作业分析
本次作业在上一次作业的基础上为了引入流水线架构思想,参考实验代码,增加了RequestCounter类。并且此次作业我采用了动态分配方式,让乘客每走一段之后进行判断,如果没有到达目的地则将其打回到waitQueue中,更改fromBuilding
、toBuilding
、fromFloor
、toFloor
,之后再根据第二次作业的调度规则将其分到对应的processingQueue中。其余的架构设计与延续了上一次作业。
绘画UML类图以及UML协作图
本单元三次作业的架构我始终没有重构过,整体架构一致,仅仅在方法实现上进行了增量开发,基本上都延续了本单元第一次实验课的设计架构,故在此只以功能最全面的第三次作业为例绘画类图以及协作图。
第三次作业UML类图
第三次作业UML协作图
四、bug分析
-
在本单元第一次作业强测没有测出bug,性能分也还可以,但是互测被找出了时间戳不完全递增的bug,随后我加了一个OutputThread类来使线程安全,这个bug就解决了。
这次作业我并没有hack别人。
-
第二次作业是我得分最惨的作业,由于那周一遍过了中测,就没继续做好测试,性能也没优化,导致强测有一个点CTLE,互测也因为这个bug而被hack了两次,并且性能分很低,进了b房。之后分析原因,发现是代码中多了两次没有必要的notifyall,删了之后bug就解决了。
这次作业我运用了第一次作业找出我的时间戳不递增的bug的测试数据,hack到了两位同学。
-
第三次作业的性能得分还算不错,互测也没被找出bug,但是强测还是CTLE了一个点,分析之后发现还是因为notifyall有一处多余,这两次都遇到了这个bug,让我长了记性,以后不能随意写notifyall了,每一次都要有加的道理,否则会产生轮询,使cpu占用资源过大。
这次作业我构造边界数据,hack到了一个也是有CTLE问题的同学。
综合来看,本单元因为多线程的不确定性,导致了一个数据在本地能测出程序bug的情况下,交上去几次都hack不到人的情况,这加大了本单元的hack难度。
五、心得体会