Part-1:对锁与同步块的理解与使用
1.锁:
在本单元的迭代中,我在HW5给电梯加了6把锁(HW7增至12把),锁的类型为Object,用于控制电梯的等待与唤醒。锁的实例在调度器实例化的时候生成,并保存在调度器中,并在调度器创建电梯对象时将对应锁的引用“发给”电梯。电梯发现没有指令的时候wait,调度器给电梯发指令的时候notifyAll。
2.synchronized关键字
synchronized关键字可用于控制对共享对象的互斥访问。比如在对等待对列的操作时,给操作方法加上synchronized关键字,可以确保共享对象的读写正常。
3.读写锁
在本单元某次理论课介绍了读写锁后,我在作业中大量使用了读写锁。读写锁相对synchronized关键字的巨大优势就时浅显易懂,使用方便,而且不容易产生一切诡异的bug。个人非常喜欢使用读写锁,在代码中几乎为所有涉及多线程读写的共享对象及数据配备了读写锁。
Part-2:UML类图以及各次作业之间的变化
HW5:
HW5是一个经典的生产者消费者问题:InputThread生产指令,Elevator消耗指令,而Sheduler相当于托盘,负责给各个电梯分发指令。电梯中保存有各种运行时必要的参数。
RequestQueue:
请求队列,内部有一个保存指令的数组,以及读写锁wrlock,和队列是否终止的的信号end
getTopRequest():获取队首元素,并在获取后删除该元素
addRequst():向队列中添加一个元素
setend():将end信号设为终止
remove():删除某请求
Elevator:
电梯类,内部属性有:电梯id,电梯当前位置positionnow,电梯速度speed,电梯最高楼层maxfloor,
电梯最低楼层minfloor,电梯等待队列myWaitQueue,最大人数maxPeopleNum
run():线程运行
checkrun():在运行向下一个楼层之前决定是否要继续运行,以及是否要对方向进行调整。
checkin():检查并将需要进来的乘客放进来
checkout():检查并将需要出去的乘客放出去
Scheduler:
调度器,负责接受指令并分发指令,内部属性有:waitQueue主等待队列;processQueues电梯单独队列;elevators电梯;threadpool线程池,用于储存线程;num计数器;elevatorlocks电梯锁
run():线程运行
get():获取指令并分发指令
checkRunnable():检查所有电梯状态,并决定是否要终止线程
InputThread:
输入线程,用于读取数据
Mainclass:
主函数
HW6:
新增需求及应对:
1.需要对指令进行调度
在HW5中,由输入指令给出电梯号,而在本次作业中不给出电梯号,需要我们自主分配。在一开始我自己设计了一个调度算法,但是由于算法本身以及实现过程中的一些缺陷,在bug修复的时候被我越改bug越多,最终迫不得已,采用了计数器模6这种简单粗暴的算法。
2.需要进行Reset
1.reset指令的出现意味着指令要进行二次调度,即换乘。换乘则意味着起始位置的变化,而官方输入包无法对起始位置进行修改,所以我对personRequest进行的了改进,即新建了MypersonRequest类,增加了对setFrom方法以对起始楼层进行修改。
2.Elevator类中新增reset方法,以对reset指令进行处理
3.如何换乘?将Elevator电梯里的人(其实是请求)修改起始楼层,打包发给scheduler线程,让调度器进行重新调度。
HW7:
新增需求及应对
1.双轿厢电梯:
如何实现双轿厢?在调度器里先初始化12个电梯,平时电梯不用就在那里睡觉,不必理会。即在我的实现中,电梯是不是双轿厢仅在于调度器给不给另外对应的电梯发指令。当然,电梯内部也有相应的标志数来反映电梯的类型。其中内部的细节就不赘述了。
2.新的Reset类型:
在Elevator类中Reset方法中通过识别Reset指令的类型来决定进行哪种reset操作。
Part-3:UML协作图展示(U2最终版)
UML协作图
Part-4:HW7中如何防止电梯相撞:
在相撞问题上,我们有两种情况
1.一个电梯即将进入,一个电梯在换层楼层等着。
一个电梯给另一个电梯发出移出信号,另一个电梯移动
2.两个电梯迎面向换层楼层过去
两个电梯竞争换层楼层的进入权,如果A竞争成功,则B放弃进入,并原地等待,同时将A移出信号置1,以便能在A完成工作时离开换乘楼层。
Part-5:Debug方法
1.print大法
不用多说,各种打印一些标志,数据,以查看程序运行状态。
2.保守性编程
先追求正确,再追求性能。把一些复杂的模块抽离出来,比如调度策略。先用最简单的平均分配,在确保其他版块没有问题再逐步优化。以及先把所有共享对象的方法都加上synchronized的关键字,几乎把程序搞成一个“顺序执行”的程序,然后慢慢分析,慢慢删。
Part-6:心得体会
纵观本单元,我这单元的表现可谓是相当差劲,两次进入了B房。问题当然是我对多线程并发理解不够深刻,架构不够清晰,以至于在第六次作业BUG修复时被迫重构,相当痛苦。但在不断的摸索下,我学习到了对于共享对象的管理,以及对于线程的调度,和各种锁的使用,还是收获颇多的。
同时,在这里我也建议课程组将读写锁相关知识放到第5次作业前给同学们介绍,相比让人困惑的synchronized关键字,读写锁简直是我心目中的白月光!
··········