309
社区成员
发帖
与我相关
我的任务
分享OO 第二单元的主题是多线程电梯调度。三次作业主要围绕电梯捎带和运行、电梯调度、维修(升级)状态转换展开。
第五次作业中,我的锁集中在 RequestQueue 类中的各个方法。作为“生产者——消费者”模型中的“托盘”,RequestQueue 使用 synchronized 关键字修饰该类中实现的方法,保证对电梯线程的等待队列的增删改查是线程安全的。
任务中涉及到必须调用 Thread.sleep() 的情况时,我将 sleep 写在同步块之外,这样可以尽快释放锁。
第六次作业取消了乘客指定电梯的能力,转而需要我们自行设计调度策略来分配电梯给乘客,追求性能的最大化。
根据解耦的思想,我设计了 ElevatorInfo 类,传递电梯的各种信息(包括它的 RequestQueue)给调度器。调度器据此来计算分数,以分配最优的电梯。通过这种方法,调度器在计算分数时,不需要获取 RequestQueue 的锁,这样就不会阻塞其的操作,提高了并发度。
第七次作业引入了双轿厢电梯,并规定 F2 为换乘层。
我设计了 Shaft 类,并要求同一个电梯井内的两个轿厢若想进入 F2,必须获取 F2 的锁,以避免轿厢发生碰撞。
F2 的锁是主动获取、被动释放的。也就是说当一个轿厢试图进入 F2,必须先提醒电梯井的另一个轿厢:如果另一个轿厢在 F2,其移动一层,并释放 F2 锁。当电梯已经进入 F2 且没有后续动作,它会保持在 F2,直至另一个轿厢唤醒它,便交出 F2 的锁,然后移动一层。
这样做可以最大限度的避免电梯空跑和轿厢相撞。
三次迭代作业中,我采用的是 LOOK 策略:电梯将一直沿着同一个方向运动,当且仅当轿厢内乘客会在这个方向的某一层下电梯,或是这个方向的某一层有人需要上电梯。
这个策略相比最基础的“一路走到头再折返”而言,可以减少很多电梯空跑的情况,以节约电量,提高性能分。
DispatchCenter 和调度策略调度器负责接受输入线程 InputThread 的输入,并维护了 12 个电梯的 RequestQueue 和电梯信息 ElevatorInfo,用于计算分数。
调度器根据计算的分数将乘客放入对应电梯的 RequestQueue 中,并 notify 以唤醒电梯线程。
或者当电梯在未到目标楼层时下客的情况下,将这些乘客重新分配到别的电梯。在这种情况下(比如维修、升级前下客),为了避免重新分配方法中的 wait 拖延时间导致维修(升级)超时,我选择临时开一个新的线程,异步地将这些乘客塞给 DispatchCenter 来重新分配。
对于需要双轿厢情况下需要换乘的请求,我选择分两步进行调度:第一步根据出发楼层调度一个电梯。当电梯运动到换乘层时,下客,并将出发楼层改为 F2,重新分配。
第六次作业中,我虽然将负载作为一个因素引入了计算分数的逻辑中,却没有硬性要求电梯的等待队列不能超过多少人。当五台电梯都在维修时,所有请求都会堆积到剩下的一台,并且无法重新 Receive。这就导致其它电梯维修完成后完全空闲,“一方有难,八方围观”的情况出现()
我在调度方法中限制:如果计算出来的最优电梯已经达到设定的负载上限,就忽略这轮计算,并 Thread.wait(100),再重新求最优电梯。这样就可以避免上述情况的发生。
多线程无法使用断点调试进行 Debug,因此我选择古法分析:System.out.printf()。
对于可能出现 Bug 的地方,我用打印关键变量的方式来观察是否出现 Bug。
此外,我也将任务要求和代码喂给 AI,用 AI 辅助 Debug。
线程安全不是靠每一个方法都加入 synchronized 关键字来实现的。关键是理清哪些东西属于是需要进行同步保护的。比如 RequestQueue,它属于典型的“生产者往里面加东西,消费者从里面取东西”的类,这种就需要仔细审查需要 synchronized 的情况。
对于多线程编程,有一个好的层次化设计是非常重要的。ElevatorThread 只管实现电梯的状态机,以及电梯自身的一些方法,比如移动、开关门等。ElevatorCtrl 则负责根据电梯当前的信息进行决策,给电梯返回一个行动指令。这样电梯线程只需要执行指令即可,实现了解耦。
DispatchCenter 是整个系统的大脑,也是一个中转站,负责把输入解析并给到对应的类中处理。这也是层次化设计的一个体现。
本单元中,我使用的主要是 Gemini 和 Google AI Studio 的 Gemini 模型。均为网页对话,没有使用 Agentic Coding。我认为网页对话可以在某种程度上限制我对 AI 的使用,尽量只与其交流思路和框架,以及代码纠错。用 Agent 可能会不知不觉中用 Tab 写完了整个代码(
第二单元对我而言是第一次接触多线程编程,我认为很有价值。实验课的代码给我们提供了很好的框架和思路,但是感觉引导略有不足。
我的建议是: