面向对象第二单元博客
面向对象第二单元博客
设计目标
这个单元的目标是模拟多线程实时电梯系统
第五次作业
设计要求
第五次作业主要模拟新主楼ABCDE座每座有一台纵向电梯的运行
类的设计
本次作业主要采用生产者-消费者模式,设计的共享对象相关的类有:
Waitqueue
等待队列,每个电梯共享一个该类的对象,调度器和输入队列共享一个该类的对象elevatorQueues
管理各个电梯队列的容器,在本次作业中由调度器进行管理
线程类有:
InputThread
输入线程,负责从控制台中读入用户电梯请求,并把请求放入调度器队列中进行处理Scheduler
调度器线程,负责把从输入线程解析出的请求调度到相应的电梯队列中Elevator
电梯线程,负责模拟电梯的运行,并处理队列中的请求
电梯运行策略
本次作业抽象了一个ElevatorMode
接口,只要实现该接口的类都可作为电梯的运行策略类。电梯使用状态机的方式运行,由策略类控制电梯的下一个状态以及人员的进出。
电梯的运行策略采用了Look电梯调度算法,主要的思想为:
- 当电梯的运行方向上没有请求的时候,电梯会改变运行方向
- 电梯会捎带运行方向上的所有请求
同步块的设计
本次作业同步块的设计为:在对共享对象进行访问(包括读取和修改)的位置设定临界区,对共享对象进行加锁。
bug分析
本次作业发现的bug为
- 由于输出线程不安全导致时间戳不递增显示的问题
- 临界区的范围过大导致的性能问题
解决方式为
- 设计了一个输出线程类
Printer
,Printer
的代码如下
import com.oocourse.TimableOutput;
public class Printer {
static synchronized void println(String s) {
TimableOutput.println(s);
}
}
- 缩小了某些类的临界区的大小,将大的临界区拆分成多个小的临界区
第六次作业
设计要求
第六次作业在第五次作业的基础上,新增了横向的电梯以及新增电梯的请求。
类的设计
第六次作业为了满足横向的电梯要求,将Elevator
类拆分为BuildingElevator
类和FloorElevator
类,两种类使用不同的电梯调度策略和不同的状态,两种类的实例都共享了一个WaitQueue
类的对象作为共享对象,保证请求队列和电梯一一对应。
同时,为了满足新增电梯的要求,新增线程类ElevatorAdder
用来处理新增电梯的请求,并管理ElevatorQueues
类的对象。ElevatorAdder
与InputThread
共享了一个WaitQueue
类对象以实现从输入中得到新增电梯的请求。
电梯的调度策略
本次作业的两种电梯的均使用状态机的方式运行,由策略类决定电梯的下一个状态以及人员的进出,横向的电梯的调度策略的主要思想为:
- 由于电梯是环形运行,电梯捎带所有的请求
- 电梯内部无请求时,选择一个外部的请求并决定电梯运行的方向
bug分析
这次由于阅读指导书不仔细忽略了可以在同一层新增多个横向电梯的情况,导致该情况下程序产生空指针异常而无法停止。
第七次作业
设计要求
第七次作业在前两次作业的基础上,新增了乘客可能需要换乘的情况以及横向电梯停靠的楼座有限制和电梯运行速度和容量的自定义。
类的设计
为了满足横向电梯停靠的楼座限制,由于调度器可访问的对象只有电梯的队列,并且能保证电梯与电梯队列的一一对应。故在初始化电梯时将可停靠楼座限制作为电梯队列的属性,调度器线程根据该属性分配合适的请求至电梯队列中。
为了实现乘客的换乘,引入了CoScheduler
线程,该线程的作用为:
CoScheduler
与Scheduler
共享两个HashMap:arriveMap
和requestMap
,arriveMap
的Key为乘客的ID,value为一个boolean值,该HashMap保存了各个乘客是否到达的目的地;requestMap
的key为乘客的ID,value为乘客的原始请求,该HashMap保存了所有乘客的请求CoScheduler
与所有电梯共享了一个WaitQueue
类的对象,当电梯有人员出电梯后,便将该人员的请求加入队列中,由CoScheduler
线程判断该人员是否到达了目标地,若没有到达,则为其生成下一个电梯请求并分配到指定的电梯中Scheduler
根据换乘要求为乘梯的请求生成第一个纵向电梯的请求,之后的换乘请求和到达的判定均由CoScheduler
负责- 本设计没有考虑电梯的速度和容量,对满足条件的电梯采用随机分配的方式进行请求的分配
bug分析
本次作业没有被Hack,挺好的。