第五次作业
1.题目简述
本次题目要求实现单部电梯,且要求有捎带功能。
2.实现思路
使用生产者-消费者模式。Producer类、Storage接口、Elevator类分别为生产者、托盘、消费者,Storage_Look类实现了Storage接口。Producer不断获取请求并传给Storage;Storage负责维护请求,并通过一定策略来调度电梯Elevator;Elevator不断从Storage获取接下来要去的楼层,并前往,到达目标楼层后实现上下人等操作。
单部电梯的调度算法有许多种,其中LOOK算法速度较快且稳定性高。其调度策略为,在最顶层和最底层之间来回运行,遇到上的请求就把人接上,遇到下的请求就把人放出;当发现电梯所移动的方向上不再有请求时立即改变运行方向。在作业中我使用的就是LOOK算法。
3.代码架构
由于Storage要进行电梯调度,而电梯调度算法有多种,所以一开始设计时,将Storage设为接口,先编写一个类(实现Storage接口)实现最简单的调度算法(FCFS算法),来测试电梯整体的功能。整体功能测试正确后,再编写新的类(同样实现Storage接口)来替代员原来的调度算法(选择了LOOK算法),提升电梯性能。这样如果以后使用新的调度算法,也可以非常方便地替换。
StorageLook类既承担了生产者-消费者模式中“托盘”的角色,又承担了电梯的“调度器”的角色。根据单一职责原则,一个类应该只专注于一个工作。应当将StorageLook类拆分为两个类,一个负责作“托盘”,一个来完成调度策略。这样做也可以在选择其他调度算法时,使做“托盘”的类可复用。
4 线程安全
本次代码中,多线程的共享变量均在StorageLook类中,对共享变量进行读写的方法均加了同步锁。
一开始我的程序结束不了,电梯线程一直在wait,后来写了如下退出流程。首先Producer检测到null后,退出循环,通知StorageLook,结束自己的线程。StorageLook得知没有新的输入后,电梯继续完成现有请求,现有请求全部完成了,电梯再获取请求时,StorageLook给其返回一个特殊值,通知电梯线程结束。
第六次作业
- 题目简述
实现多部多线程可捎带调度电梯的模拟,电梯数量由输入的第一个数字确定。电梯楼层包含了负数层。
2 实现思路
在第五次作业上做扩展,仍然使用生产者-消费者模式。不同的是,生产者-消费者是一个托盘多个消费者,而我作业里是多个托盘,一个消费者对应一个托盘,生产者均衡第将请求放到不同的托盘里。
单部电梯仍然使用LOOK算法来调度。在生产者进行请求分配的时候,主要根据一个评估的函数来判断哪部电梯相对最“闲”,将请求分派给它(对应的托盘)。
这里评判“闲”的标准是:这个电梯的所有请求数,加上在电梯外的人*0.03,这个值越小表示越“闲”。即,某个电梯的请求数最少,就选它来接收新请求。电梯请求数都一样,就看谁电梯外面的人更少。
在原来的LOOK算法的实现中,由于我编写的数据结构的原因,楼层只能是正数。由于本次作业楼层出现了负数,于是编写了Parse类,将题目中的-3到-1、1到16层映射为1到19层,这样原来的LOOK算法可以直接复用。输出的时候再映射回去。
使用My Request来代替官方的Person Request,可以更方便地设置一个请求里的属性。
3 代码结构
主要区别是增加了Parse类和MyRequest两个类,在Producer中维护Storage数组,Storage接口增加了simulate方法用来计算电梯忙不忙。
线程安全
和上次作业一样,本次作业的共享变量存储在StorageLook中,相关方法使用同步锁进行保护。
Producer线程退出时,通知每一个StorageLook,后面的退出流程和上次作业一样。
第七次作业
1、 题目描述
实现多部多线程可捎带调度电梯的模拟。电梯分为A、B、C三类,每类电梯有自己的停靠楼层、运行速度、载客量。一开始三类电梯各有一个,后续通过输入动态添加电梯。
2、 实现思路
使用Master-Worker模式。Input类负责获取输入的请求,将请求传给RequestPool类。RequestPool类维护一个请求队列,可以添加和获取请求。Manager类从RequestPool中获取请求,通过Analyse类计算调度策略后,将请求分派给指定的StorageLook类。每一个StorageLook类使用LOOK算法对单部电梯进行调度。
一些请求需要换乘才能到达目的地。在Analyse类计算调度策略时,将换乘楼层保存到请求中。电梯将人从起始层运到换乘层后,再次将请求发送给RequestPool,实现请求的第二次的分配。
Analyse类的分配策略设计得较为简单。若能直达,就直达,选择可直达电梯中最“闲”的(使用上次作业中的simulate方法)。若不能直达,则通过一次中转到达目的层,选择起始层到中转层最闲的电梯。
3、 代码结构
Storage和Analyse接口使得请求分配策略和电梯运行的策略具有可扩展性。
MyRequest类几乎是为了满足我的分配策略而设计的,复用性很低。应该将MyRequest作为父类,实现最基本的属性和方法,然后编写一个子类来满足本次分配策略。
4、 线程安全
本次作业线程安全出现的问题主要是最后程序退出时的问题。输入截止,而且RequestPool没有请求后,各个StorageLook类中也没有请求,本以为这时程序可以结束了,但其实有的需要中转的请求刚离开StorageLook,电梯正在输出,然后还要再返回给RequestPool。也就是说,输入截止、RequestPool为空,并不代表没有新的请求。解决办法是,在Manager类中记录所有正在进行的中转请求,只要还有中转请求,就不会停止。