BUAA_OO_第二单元作业
BUAA_OO第二单元作业
总述
本单元主要考察多线程的交互,线程的安全问题。其次是对电梯调度策略的设计。该单元里我的调度策略似乎不太适合强测的样例。就不多阐述我的调度策略。
第一次作业
架构思路
本次作业主要由三部分组成:电梯线程、数据处理线程和乘客表。
电梯线程:电梯线程主要负责从乘客表将乘客取出电梯,并将乘客带到目标楼层。电梯主要有三种工作状态:
-
运行状态:在楼层间运行,每层400ms,到达后输出楼层信息。运行状态是电梯工作时的主要状态,只要有乘客在电梯就会保持运行。
-
停靠状态:当电梯到达一个楼层时,需要判断是否需要在本楼层停靠,停靠条件有:(用一个flag标记此时电梯门的开关状态,避免重复开门)
-
电梯内有乘客到达自己的目标楼层,要下电梯。
-
电梯未满,电梯外有乘客要上电梯。先下后上,保证电梯人数不大于满载人数。
-
-
等待状态:如果乘客表中没有乘客,也没有收到“输入结束”的标志,电梯在运送最后一名乘客的楼层关门等待,直到有新乘客出现。
线程安全设计
基于生产者消费者模式,本次作业的线程安全问题主要是电梯线程和数据处理线程(调度器和输入线程)对乘客候乘队列的改写,在设计线程保护时,选择只在管理乘客表的类中进行加锁,而不在电梯线程或者调度器中加锁。乘客表类中,调用从乘客表取人进入电梯和从输入指令中将人放入乘客表这两个函数时,需要将乘客队列进行加锁。为了避免轮询,我们考虑到电梯和调度器本身是不容易唤醒的,所以当总等待队列和候乘队列没人时,我们将队列挂起,直到有人加入队列再唤醒队列。
调度器设计
调度器只负责总等待队列的调度,输入线程将请求放入总等待队列,由调度器负责分配这个乘客该去往哪个楼座队列。
UML类图
UML协作图
程序Bug
我设置主请求后,去主请求出发楼层的路上没有捎带,就浪费了时间,导致第一次强测就有三个点出现RTLE。
Hack策略
这次有些同学的容量特判用的小于等于号,导致实际容量成了7人,应用小于号,才能保证是6人。还有就是没有上好锁,导致出现了线程错误。这样只要在同一时间大量投入同样请求的乘客就可以测出来。
第二次作业
架构思路
第二次作业添加了横向电梯,且可以添加多部电梯,但因为所有乘客的请求都可以一次性完成,实际上与第一次作业没有差别,只不过纵向运行的变成横向了,同时因为有多部电梯可以运输,我们采取自由竞争策略,谁能先接到乘客,谁就运送乘客。
线程安全维护
本次作业的线程安全问题和上一次相同,都是对请求队列的添加和删除乘客造成的线程安全。
调度器设计
与第一次作业一样,调度器只负责总等待队列的调度。
UML类图
UML协作图
程序Bug
在要接主请求的时候没有及时纠正电梯运动方向,导致有可能接不上主请求,虽然强测没有测出来。
Hack策略
没有hack过
第三次作业
架构思路
这次乘客有了换乘需求,且电梯属性有了改变,环形电梯有些楼座不停靠,需要注意,还好我们初始在一楼有个可以达到所有楼座的环形电梯,这保证了我们最多坐一次横向电梯就可以到目标楼座。于是我们只让乘客最多坐一次环形电梯。在调度策略上我们仍然采取自由竞争,这样运行地快的电梯就可以得到充分的利用。
线程安全维护
本次作业的线程安全问题和上一次相同,都是对请求队列的添加和删除乘客造成的线程安全。但有个问题就是要重新设计避免轮询的条件,因为我们的乘客有换乘的需求,所以需要把达到了换乘楼层的乘客再放回总等待队列,这会导致我们的调度器线程再输入结束后不能立马杀死,所以我们需要把调度器线程挂起,直到有人再进去总等待队列,再把调度器唤醒。
调度器设计
有个问题就是要重新设计避免轮询的条件,因为我们的乘客有换乘的需求,所以需要把达到了换乘楼层的乘客再放回总等待队列,这会导致我们的调度器线程再输入结束后不能立马杀死,所以我们需要把调度器线程挂起,直到有人再进去总等待队列,再把调度器唤醒。这次,我们需要做个计数器来统计剩余调度次数,通过计数器来把调度器线程杀死。
UML类图
UML协作图
程序Bug
开门特判写在了下乘客的方法,应该写在上乘客的方法中,导致了出现电梯吃人事件。
Hack策略
无
心得与体会
本单元的电梯作业相较于第一单元来说更简单,我们只需要注意好线程安全问题和避免轮询,如何协调好wait和notify的使用。然后就是电梯的捎带策略和调度策略,由于多线程不好做测试,我们在写代码的时候一定要注重自己的逻辑严密性,最好先做个图,明白自己的电梯怎么载人和运输乘客的。本单元更多考察的就是细心和对多线程运行的理解。