OO_第二单元总结
第一次作业
乘客的请求信息:起点层和终点层不同,起点座和终点座相同。如1-FROM-A-1-TO-A-2
思路:采用LOOK策略。若同方向上没有请求且电梯里的乘客的目的地都在反方向,则转换电梯运行方向。(捎带前往目的地与电梯运行方向相同的乘客)
UML类图
自己程序的bug
输出时间戳没递增。
第二次作业
乘客的请求信息:[起点座 == 终点座] + [起点层 == 终点层] == 1
,即楼座和楼层两者中有且仅有一个相同。如1-FROM-C-7-TO-A-7
思路:横向电梯的调度策略与LOOK策略类似。判断方向的方法:若顺时针更近则方向为右,若逆时针更近则方向为左。(优先捎带前往目的地与电梯运行方向相同的乘客,再捎带在当前层等待进入电梯的乘客)
采用自由竞争,让电梯去抢请求,哪个电梯快,乘客就坐哪个电梯。
UML类图
自己程序的bug
终于没有bug了,高兴TvT。
第三次作业
乘客的请求信息:[起点座 == 终点座] + [起点层 == 终点层] != 2
,即楼座和楼层不会同时相同。如1-FROM-A-4-TO-C-3
思路:将请求分成好几段(最多三段),当完成一段请求后,就根据该请求的下一段请求信息put进相应的processingQueue,直到完成所有段则完成了一个乘客请求。
需要用Counter来记录完成的请求数量,所有的请求完成后,才结束所有电梯线程(记得notifyAll)。
UML类图
自己程序的bug
忽略了可达性问题,强测和互测都被hack烂了,快乐的时光总是那么短暂。
UML协作图
同步块的设置和锁的选择
明确共享对象以及哪个线程对共享对象进行写操作,哪个线程对共享对象进行读操作,在这些操作中访问共享对象时需要加上锁,确保在同一时刻只有一个线程在访问共享资源。(使用synchronized加锁)
在第一次作业中创建了共享类对象,所以在加锁的时候,只需要在该类的方法上加上synchronized就可以了,这样方便了很多,也比较难出错。
三次作业中的共享对象都是waitQueue和processingQueue,所以在对它们俩进行操作时要多上点心。
锁与同步块中处理语句之间的关系:若线程A进入同步块获取锁后,其他线程还想要进入同步块获取同个锁是不行的,只能阻塞在外面,直到线程A退出同步块释放锁,其他线程才可以获取锁。
调度器设计
以生产者消费者模式的角度来看,生产者(InputThread)生成A-O类的请求,我的调度器会负责将这些请求分配给相应的消费者。如将A类请求分给处理A类请求的消费者A(Elevator),将B类请求分给处理B类请求的消费者B......
第一次作业:只有纵向电梯,所以只需要根据电梯的楼座ID分配。
第二次作业:引入了横向电梯,根据横向电梯的楼层ID或纵向电梯的楼座ID分配。
第三次作业:和第二次作业一样。
发现别人程序bug所采用的策略
用测试自己代码的数据拿去测试别人(重复利用 😄),然后再稍微写点自己认为比较强的数据(好像只能这样了)。
心得体会
觉得架构上比较有优势(参考了实验课和讨论区提供的思路),所以比较少出现线程安全或死锁问题,比较常遇到的问题是轮询。还有就是在写多线程的时候可以稍微注意一下原子操作。