文章目录
第5次作业
目标
本次作业需要实现一个多线程电梯系统,共有6个电梯,11楼,每个乘客钦定乘坐哪个电梯,同时起点终点确定。
从标准输入中输入请求信息,请求信息按时间输入,程序进行接收和处理,模拟电梯运行,电梯操作有:开关门,移动,上下客,将必要的运行信息通过输出接口进行输出。
本次作业是多线程的第一次作业,不涉及乘客间与电梯间的信息交互,只需要处理好电梯和乘客等待表中的可能的线程安全问题即可。
基本思路
我的主要类有三个:InputHandler,RequestList,Elevator。其中:
- InputHandler处理输入,同时根据输入将乘客分配到合适的RequestList中。
- RequestList存储某一电梯的乘客等待表
- Elevator实现电梯,与其所绑定的RequestList进行交互,处理用户请求。
InputHandler和Elevator同时有对RequestList的访问,故在RequestList类中添加锁,所有对RequestList的操作都通过锁,锁通过sync语句块实现。
电梯调度使用LOOK算法,可以得到一个比较好的效果。
UML类图
UML协作图
出现的bug
强侧通过,互测被hack了一次,原因是锁没加好,Strategy类中对RequestList的访问不安全。
第6次作业
相比于第五次作业,第6次作业增加了电梯的reset操作以及新增调度器的需求。
难点分析
在这次作业中,可以发现线程的访问关系形成了一个环,这就导致了很多bug的产生。
基本思路
由于访问关系成环,为了防止死锁,考虑生产者消费者模型,新增PersonBuffer类和Distributer类,分别用来作为交互中间件与分配器。
- InputHandler向PersonBuffer中添加人,同时Elevator在reset开始时也向PersonBuffer中添加人,为生产者
- Distributer从PersonBuffer中加人,为消费者
在这种设计下,可以较好的分割开类的功能,以减少bug的产生。
对于分配策略的选择,我使用影子电梯实现,具体方法是通过继承Elevator类并替换其中与时间计算相关部分来达到简易扩展,每次调度请求产生时,分配器根据每个电梯的状态复制影子电梯,并根据结束时间将请求放入结束时间较少的电梯中,以达到最大优化效果。
在这次作业中我选择使用ReentrantLock来实现锁的机制,相较于sync语句块其具有较强灵活性。
UML类图
UML协作图
出现的bug
强测通过,互测被hack一次,锁没加全,在Elevator类中执行Action时的状态和获得Action的状态不同,进而导致死锁bug产生。
第7次作业
相比于第6次作业,第7次作业新增了双轿厢电梯。
难点分析
- 双轿厢电梯不能同时停靠换乘楼层,这给电梯调度产生了额外的复杂度
- 双轿厢电梯在使用影子电梯时不是很好实现,容易产生bug
基本思路
对于停靠楼层的限制,我新增了一个锁来表示楼层占用情况,电梯想要进入换乘楼层则必须获得该锁,否则不能进入换乘楼层,这样就可以实现双轿厢的两个轿厢不碰撞。
由于影子电梯实现过程中带来了很高的复杂度,很容易产生bug,故我在这次作业中没有实现影子电梯的调度策略,而是通过电梯的参数,加权求和来近似求出电梯结束时间。通过调整权重,可以在时间、电量等多个性能指标中实现一定的平衡。
UML类图
UML时序图
最终代码量
bug分析
这次作业中我通过了强测与互测,没有bug产生,由于没有实现影子电梯,可能产生的bug少了很多,这对bug的查找与分析带来了便利。
总结
这个单元不变的就是要一直处理多线程程序的特性与bug,包括进程通信方式,如何交互等,还有基本的架构也是没有改变的,都是输入,分配,电梯运行,调度策略这4个部分,其中的细节变化还是比较大的,从钦定电梯到自由分配再到多轿厢电梯,变化虽然大但又很大一部分是在做增量,想清楚后写起来还是不算非常困难的。
这个单元使用到了多线程的相关知识,多线程程序是非常容易出错的,但是其复现又十分困难,我在debug时主要使用输出中间过程的方法,从中间过程去反推bug出现的位置,虽然该过程比较耗费时间,但是的确是少数有效的debug方式。
感觉多线程编程想要避免bug还是很困难的,从串行程序的线性运行变成并行程序的多个线程并行,是相当于升高了好几个维度,需要考虑的东西多了很多,导致线程安全是困难的,这对代码能力提出了很高的要求。
在这个单元中评测机帮助我发现了很多bug,但是对于并发程序,再多的测试也不能保证其正确性,指不定哪里执行顺序一变就又产生了新的bug,防不胜防。
besides,感觉理论课和作业严重脱节,理论课给我的感觉就是讲的过于细致,大讲各种设计模式、架构模式,讲java中多线程的具体细节,然后就给出一堆多线程的问题,令人迷惑,感觉不如把多线程的基本思路,多线程错误类别(如原子性违反、顺序违反)这些东西讲讲清楚。