一、总结分析三次作业中同步块的设置和锁的选择,并分析锁与同步快中处理语句之间的关系
作业1:
同步块设置在dispatch类中,dispatch存放了输入线程和电梯线程之间的共享队列,其实质是电梯线程的外部请求队列。锁即dispatch类对象。同步块中处理语句,主要聚焦于外部请求队列的读和写,上锁就是读写互斥、写写互斥。
作业2:
新增了输出线程的安全锁和同步块,防止输出not in order。
其余的与作业1类似。
作业3:
新增了Manager类的同步块和锁。manager是管理某座or某层的电梯的,里面有该处的电梯队列。之所以要对此电梯队列进行读写互斥加锁,是因为现在不止输入线程会调用manager,电梯线程在处理完请求的一阶段后也会调用manager中的电梯队列,从而可能导致线程安全问题。
二、总结分析三次作业中的调度器设计,并分析调度器如何与程序中的线程进行交互
三次作业主调度器都为scheduler,作业2、3新增manager用于分配分配到该座or该层的电梯请求到具体的电梯中。
作业1中,直接利用input线程调用scheduler将请求分配给相应电梯的dispatch(即外部请求队列)。
作业2中,同上所述。
作业3中, 在scheduler中对请求进行分析,变为1-3步的请求PersonAsk,再同上进行处理。
三、分析和总结自己三次作业架构设计的逐步变化和未来扩展能力
作业1采用生产者消费者模型,UML类图如下,input线程调用调度器scheduler通过仓库dispatch跟elevator进行交互。
作业2采用生产者消费者模型,UML类图如下,input线程调用调度器scheduler和manger类通过仓库dispatch跟elevator进行交互。使用manager类管理某座or某层的电梯队列。使用电梯工厂新增竖向和横向电梯。
作业3采用流水线架构。UML如图,新增PersonAsk来封装流水线请求,新增RequestCounter来检验流水线请求完成情况,input线程和电梯线程仍然使用scheduler处理请求。
三次作业的时序图为:
作业1:
作业2:
作业3:
四、分析自己程序的bug
分析未通过的公测用例和被互测发现的bug:问题特征和修复办法
第一次作业:
公测pass。互测则是输出线程的不安全,即没有进行输出安全类的封装。
第二次作业:
公测互测都是一个bug,即在横向电梯运行的时候,我采用的look策略当判断前方总是有人要上电梯时,会停不下来。这是电梯具体运行策略的bug。
修复办法:对电梯空载走3步进行判断,如果是,说明本该3步以内上电梯的人没有上,即不该走了,该反向运行。
第三次作业:
公测互测都是一个bug,来自于第二次作业的bug修复,即当反向时,没有在反向的那一楼座停留,判断是否有乘客要进入,而是直接反向走一步,所以该上车的永远没上车,RTLE。这还是电梯具体运行策略的bug。
修复办法,将阈值改为4步即可。
五、分析自己发现别人程序bug所采用的策略
列出自己所采取的测试策略及有效性
我采用的是单元测试策略。即针对竖向请求、横向请求、换乘请求各自进行测试。如果单元测试都通过了,那么通过控制台的输出来判断是否存在线程交互的安全问题。如果没有,即代表代码无误。
分析自己采用了什么策略来发现线程安全相关的问题
通过控制台输出的方式,即在每个线程的run结尾处打印线程结束语句,判断该线程是否即时结束,如若不然,则可以判断其出现死锁等线程安全问题。
分析本单元的测试策略与第一单元测试策略的差异之处
第一单元是根据题目要求进行完备性的样例覆盖测试,对于输入输出有着较为明确和系统的规范,可以进行泛用。
此单元题目要求比较简单,逻辑并不复杂,线程间的交互也比较明确,但解决的策略多样,不同调度算法可能有不同的bug,可以用完备的单元测试样例来检测bug,也可以看实际的代码来研究,从而进行针对性的bug测试。
六、 心得体会
线程安全:
一定要注意wait之后是否能够被唤醒。
sychronized块尽可能写小。
层次化设计:
以类为思考对象,让类纯粹,用类与类的交互来解决问题。第1第2次的作业可以说是一脉相承,都是简单的生产着消费者模式,但是到了第三次架构,就需要使用流水线架构,不过本质上由于电梯就做电梯该做的事,不参与调度,所以我们主要需要在Scheduler类里面对请求进行流水线处理即可。
学会运用工厂模式和单例模式。
提取子类的共性到父类中去,将子类的特有的行为进行方法重写。