程序结构整体分析
架构展示
整体沿用分层的生产者-消费者的设计:
输入线程 - 分配器线程 - 电梯线程
(显然,输入/分配需求/电梯运行是三种并行的线程,这样的设计是很自然的)

复杂度
Class Metrics
| Class | OCavg | OCmax | WMC |
|---|
| Average | 1.84 | 3.48 | 10.05 |
| tools.Elevator | 1.63 | 6.0 | 85.0 |
| strategy.NormalPolicy | 2.9 | 5.0 | 52.0 |
- 分析可知,代码整体没有很复杂的方法,但部分类WMC过大,反映出其负担过大成为巨类的问题
- Elevator 的 WMC 明显偏高,主要原因是它同时承担了电梯实体状态、基础动作、乘客进出、特殊流程和输出封装等职责。随着第二、三次迭代引入检修、改造、回收,特殊状态流程也继续堆叠在该类中,导致其逐渐演化为巨类。
待改进缺点
- Elevator类负担太大,长度过长
- 可能可以引入设计模式来优化,但是迭代时为了避免重构没有尝试
架构设计体验与优化
第一次作业
基本是照搬oolens上的设计思路,采用分层生产者-消费者设计,电梯运行策略使用LOOK策略
- 本次作业不涉及dispatch,但为了后续的迭代仍预留了DispatchThread和DispatchStrategy
第二次作业
调度设计
选择自由竞争策略,设计了一个代价公式(理论上应该多试几组参数但现在的参数效果还不错)
private int calculateCost(ElevatorStatus status, PersonRequest request) {
int currentFloor = status.getCurrentFloorIndex();
int fromFloor = FloorUtil.toIndex(request.getFromFloor());
int distanceCost = Math.abs(currentFloor - fromFloor);
int directionCost = calculateDirectionCost(status, request);
int waitingCost = status.getWaitingCount() * 2;
int loadCost = status.getCurrentWeight() / 100;
return distanceCost + directionCost + waitingCost + loadCost;
}
- 优化:为避免少数可用电梯被瞬间塞入过多任务,在可用电梯数量较少且候选电梯等待队列超过阈值时,调度器会暂缓接收,将请求放回全局队列稍后重试。
第三次作业
调度设计
在第二次作业的基础上,新增了跨区运输的需求。
核心:分区运输怎么实现?
- 添加TransferRegistry,用以记录跨区PersonRequest的最终目的地
由DispatchThread和12个ElevatorThread共享- DispatchThread:注册跨区乘客最终目的地
- ElevatorThread:乘客下车时查询 finalToFloor,并在最终到达时清理
- 策略:对于一个请求
- 原请求有人能接:直接派原请求。
- 原请求没人能接,且它跨区:尝试拆成 from -> F2。
- 第一段有人能接:派第一段,并登记最终目的地。
- 第一段也没人能接:返回 -1,稍后重试。
线程安全设计
- 共享对象均使用
synchronized 封装访问,通过 wait/notifyAll 实现生产者-消费者协作。 - 电梯状态通过
ElevatorStatus 向调度器暴露,只提供必要的只读快照信息,避免调度器直接操作电梯内部状态。 - 双轿厢的井道约束集中在
ShaftStatus 中处理,避免两个电梯线程各自判断导致约束分散。
bug分析
第二次互测中,受到hack数据:
在同一时间,五台电梯全部收到维修请求;在它们进行维修时,涌入大量PersonRequest
- 问题分析:我的电梯没有设计延迟接收请求,于是大量Request一次性被仅剩的一台Normal电梯接收,后续无法由其它电梯分摊压力,系统退化成单电梯,最后导致超时。
- 解决:设计延迟接收,当一台电梯对应的assignedTaskQueue内Request数量到达设定的阈值,停止接收Request
第三次迭代中,如果进入双轿厢模式后,主轿厢结束委托时刚好停在换乘层,但备用车厢有接到了需要移动到换乘层的request,就会造成死锁
第三次迭代中出现双轿厢碰撞的问题
- 问题分析:对于不能同层的约束不够准确,只是通过判断对方现层数来决定自己是否移动,因此可能出现刚好一起移动到换乘层的情况
- 解决:预占目标楼层
大模型使用
- 在第一次迭代时,我对于如何让DispatchStrategy获得电梯状态感到迷惑,于是先尝试和大模型交流。结果它一直教我到处开快照,试图把架构改得面目全非,于是最后还是自己设计了同步方法。
- 但是,大模型在帮忙简化代码的方面给了我很大帮助,在它的帮助下我把超出checkstyle要求行数的类与方法成功优化为合法行数
- 后续如果想引入设计模式来继续优化,感觉也可以让大模型提供帮助
- 另外,大模型提高了debug的效率。手动找output中不合题意的部分是件很费眼的工作,然而大模型对于这种有明确规则的工作非常拿手。
心得体会
- 互测其实是很好地学习他人思路的机会,在寻找他人代码漏洞的同时,自己也拓展了题目的实现思路
- 第二单元作业后期与os课程中的进程同步互斥知识学习同时进行,学习时感觉有相辅相成之感