OO第二单元总结
一、程序设计架构
总体架构
三次作业总体架构保持一致,在初始架构的基础上进行增量的迭代开发。
第一次作业
需求:模拟多线程实时电梯系统,每座固定一部纵向电梯,处理同楼座移动请求。
代码框架:
本架构采用生产者-消费者设计模式,托盘为各楼座等待队列,为每座定义了两条等待队列,根据起点至终点的方向不同分为上行队列与下行队列,简化了电梯方向判定的算法。
代码规模:
第二次作业
需求:模拟多线程实时电梯系统,每座初始各一部纵向电梯,可增加纵向电梯与横向电梯,处理同楼座、同楼层移动请求。
代码框架:
代码规模:
对 TimableOutput 封装为OutputThread类,避免出现输出时间戳非递增的情况
第三次作业
需求:模拟多线程实时电梯系统,每座初始各一部纵向电梯,一层一部横向电梯,可增加纵向电梯与横向电梯,处理同楼座、同楼层移动请求以及换乘请求。
代码框架:
代码规模:
第三次架构与第二次基本相同,主要修改在调度器部分,增加了换乘相关操作的方法。
可扩展性分析
-
第一次作业中将电梯相关属性保存在电梯类中,后续作业自定义电梯属性时便于修改。
-
协作图
二、同步块与锁
同步块
-
仿照上机练习代码,建立了一个线程安全类
RequestQueue
,实现安全增加、修改、取出请求。
-
封装输出安全类
OutputThread
,避免出现输出时间戳非递增的情况。
锁
三次作业中,我都选择了使用关键字 synchronized
修饰的锁机制。
-
在
RequestQueue
中对方法上锁,实现队列修改的线程安全。 -
在
Elevator
和Velevator
中对访问的公共队列上锁,防止访问引发线程冲突。
三、调度器设计
在前两次作业中,调度器的核心功能极为简单,即充当“托盘”的作用,接收请求,并分配到候乘表队列中。
第三次作业增添了换乘操作,调度器多了一个“分解”请求的作用,即把换乘请求分解成多个简单请求,这样保证了电梯类只需较少改动,就能完成换乘的要求。
在请求接收方面,采取自由竞争的策略,优点是不需调度器从电梯获取信息,降低耦合性;但有可能会导致电梯接收到不能处理的请求(目标层座不可达),此时需要将请求吐出。
四、DEBUG与测试
第一次作业:
强测出现两个RTLE错误,主要是因为结束时没有判断电梯内是否清空,导致电梯线程没有结束。
第二次作业:
由于未封装输出模块,导致强测出锅,未进互测。
在debug过程中对输出模块进行了封装。
第三次作业:
强测出现两个RTLE错误,一个是纵向电梯中转向函数的缺陷导致,另一个是横向电梯吐出请求的函数,在队列中有多个需要吐出的请求的时候会死循环。
互测无bug。
三次作业中,均采用测评机+构造数据相结合的方式进行测评。
五、心得体会
-
本单元的设计关键之一在于设计线程安全类,使线程尽可能少的处于临界区之中,保证线程安全;使用锁互斥来解决访问冲突。
-
工程设计中要坚持SOLID原则,尽量保证类的功能单一,不承担过多的职责,比如调度器只负责分发请求,电梯只负责解决请求等等。
-
在电梯策略方面,我从始至终使用的都是LOOK策略,虽然性能比较平庸,但代码出锅概率也下降了很多。
-