oo第二单元作业总结
一、概述
本单元的基本目标是模拟多线程实时电梯系统,需要熟悉多线程的设计与基本操作,能够掌握线程安全知识并解决线程安全问题,最后还需掌握线程之间的交互,
强化线程之间的协同设计层次架构。
二、同步块的设置和锁的选择
第一次作业,我以实验代码为基础,使用synchronized进行同步,由于共享对象仅为PersonRequest,故直接为PersonRequest类的大部分方法上锁,将同步
块尽量最小化;为了规避bug,在所有同步块的最后加上了notifyAll()方法。
第二次作业,可能有多部电梯共享同一个请求队列,对锁的使用方法认识加深,故将大部分方法内的notifyAll()删除,仅在addRequest()方法末尾使用notify(),
在setEnd()方法末尾使用notifyAll()方法。
第三次作业,为了让程序能正确结束,使用单例模式添加了End类,在End类的sub()方法内添加了notifyAll(),在InputThread类中添加了共享对象为End单例的
同步块,控制对ProcessQueue的setEnd()操作,为了程序的简单,仍使用synchronized进行上锁,未使用课上所介绍的lock以及线程安全类进行上锁。
三、调度器
为每一类电梯设计了一个Tactic类作为调度器,其中同类型并处于同楼座或同楼层的电梯共用一个Tactic对象作为调度器;并在InputThread类中管理请求的
静态分配,以及新建电梯操作。
四、架构设计
1.第一次作业
(1)UML类图
(2)架构分析
使用生产者-消费者模型。
- 分别开设输入线程、分配线程、调度器线程。
- 输入线程InputThread读入一个请求,将存储进总的请求队列waitQueue中。
- 分配线程ScheduleThread将waitQueue中的请求分配到各个processQueue中。
- 调度器线程控制电梯处理待处理队列中的请求。其中,电梯的运行策略由Tactic类实现,简化了电梯类,增加了可拓展性。
- 使用安全线程MyOutput输出信息。
电梯使用类似ALS的调度策略。
(3)bug分析
本次作业仅在互测中发现安全输出线程的bug,导致输出信息时间戳可能不递增,后添加安全输出线程MyOutput类解决。
2.第二次作业
(1)UML类图
(2)架构分析
由于第二次作业仅添加了环形电梯,与第一次作业架构基本一致,仅在细节处做了优化;例如删除多余的notifyAll(),修改wait条件,在InputThread类中添加了
新增电梯的操作。
(3)bug分析
在互测中出现了一个轮询的bug,将wait()条件由if改为while得以解决。
3.第三次作业
(1)UML类图
(2)UML协作图
(3)架构分析
本次作业乘客请求发生变化,乘客请求可同时跨楼座跨楼层,故作如下修改。
1.重写了一个RealRequest类,为每一个乘客添加路径节点属性,以及当前所在位置和当前目标位置属性。
2.在InputThread中进行对原始请求进行静态分配,计算出合适路径。
3.在电梯类负责放出乘客的方法letOut()中添加了将乘客送往下一个等待队列ProcessingQueue的操作。
4.新增单例End类记录所有乘客是否都已到达目的地,以实现程序的正确结束。
(4)bug分析
仅在强测出现了一个RTE的bug,首先怀疑是电梯运行策略出了问题,后来发现是InputThread类中吧buildng拼成了buliding,导致添加纵向电梯的
所有操作都不成功,而且这个愚蠢的bug居然在第二次作业中就有了,只是没被任何测试发现...... 这也导致我两次强测的性能分都低于策略本应有的数值,
痛定思痛,以后绝不轻视IDE所提示的任何黄色和绿色警告!
五、hack策略
本次电梯作业肉眼判断测试结果较为困难,故而放弃了对每个程序进行单独测试,采用根据自己在设计程序时所想到的bug,直接构造数据的策略。
效果颇为不错,可见大家在多线程程序上所犯的错误还是颇有共性的,这也体现出多线程程序的程序安全和线程调度问题通常难以面面俱到地解决,需要
我们设计出尽量简洁清晰的架构以更高效地debug,以及构造良好测试数据的重要性。
六、心得体会
1.设计时尽量让每一个进程都各司其职,功能单一化,可以大大增强代码的可读性和可拓展性。
2.合理设置wait(),notifyAll(),notify(),只在必要时唤醒线程,可以有效避免轮询。
3.观察者模式,生产者-消费者模式,单例模式等各种设计模式的学习让我们得以站在巨人的肩膀上设计代码。