OO-U2 作业总结

梁家诚-74056202 2026-04-25 22:08:49

多线程编程与单线程编程的思维方式截然不同。本单元的电梯系统迭代从最初的单部固定电梯,一路迭代到需要统筹全局的调度器,再到引入临时检修状态机以及极其复杂的双轿厢换乘机制。以下是我对这三次作业的复盘与总结。

一、 同步块的设置和锁的选择

在多线程环境下,保护共享资源的安全性是第一要务。在我的架构中,主要的共享资源是请求队列(RequestQueue)

  1. 同步块与Wait/NotifyAll机制: 为了避免轮询带来的性能惩罚甚至是TLE,我主要采用了 synchronized 关键字配合 wait()notifyAll() 方法。输入线程、调度器线程和电梯线程都会尝试获取共享队列的锁。当电梯发现自己的候乘队列为空且系统尚未结束时,会主动调用 wait() 挂起;而一旦调度器将新请求放入该队列,或者输入线程向主队列投入了新请求,就会调用 notifyAll() 唤醒处于等待状态的线程去竞争锁并获取任务。 锁与处理语句的关系:同步块内部的代码必须是“快进快出”的。在同步块中,我只进行数据的读取、修改和状态的判断,绝不在此进行耗时的操作(例如电梯的sleep移动模拟)。耗时操作全部放在锁的外部,以保证并发度。

  2. 特殊的防碰撞锁(双轿厢换乘层): 在后续引入双轿厢电梯(共用同一井道,在特定楼层如F2换乘)时,基础的队列锁已经无法满足要求。两部轿厢绝对不能同时处于换乘层。为此,我专门为换乘楼层对象引入一把互斥锁。当A轿厢需要驶入换乘层时,必须先尝试获取这把锁,如果B轿厢已经占用,A轿厢则必须在换乘层外等待,直到B轿厢离开并释放锁。

二、 调度器设计与调度策略

  1. 三级流水线与交互方式: 随着作业要求的升级,我构建了“输入线程 -> Dispatcher调度器线程 -> 电梯线程”的三级流水线架构。

    • 输入线程:唯一的生产者,负责接收标准输入,将解析后的请求无脑丢入系统的“总等待队列”。

    • 调度器线程:扮演总线中枢的角色。它从总队列中取出请求,根据当前的电梯状态,将请求分发给各个电梯私有的“子等待队列”。

    • 电梯线程:纯粹的消费者。它们只关心自己的子队列,按照自己的运行策略移动并接送乘客。 这种设计实现了高内聚低耦合,调度器不需要知道电梯具体怎么跑,电梯也不需要知道外面的请求是怎么来的。

  2. 调度策略与性能权衡(时间与电量): 电梯的单体运行策略上,我采用了基于 ALS的算法,保证在同一方向上尽可能多地捎带乘客。 在宏观的调度策略上,为了兼顾运行时间和耗电量,调度器在分配请求时会综合评估各电梯的“代价”。如果某部电梯正在检修,调度器会立即停止向其分配新请求,并将该电梯被强制清空出来的乘客重新回收到总队列中,进行二次分配。在双轿厢场景下,由于双轿厢拥有更高的并发运载能力,调度器会适当给予其更高的权重。

三、 出现的Bug以及Debug方法

  1. 典型Bug分析

    • TLE与死循环:在处理双轿厢换乘或者乘客因检修被赶下电梯时,曾出现过逻辑死循环。乘客不断地被放入队列,又因为不符合上车条件被弹出,导致电梯在同一楼层疯狂开关门。

    • 线程无法正常停止:当输入结束后,输入线程发出结束信号。但由于检修状态机(NORMAL -> ACCEPT -> REPAIR -> TEST -> NORMAL)的复杂性,部分电梯在完成检修后,没有正确收到系统结束的广播,导致一直处于 wait() 状态,无法使进程正常退出。

    • 检修清空乘客异常:在收到 MAINT1-BEGIN 信号时,未能安全且原子地清空轿厢内乘客,导致部分乘客状态丢失,最终无法完成所有请求。

  2. 多线程Debug方法: 多线程的Bug通常难以复现。我放弃了传统的断点调试,转而使用时间戳日志法。通过在每一个获取锁、释放锁、wait、notify、以及状态转移的地方打印带有时间戳和线程名的详细日志,将日志导出到文件,然后写一个简单的Python脚本去验证时间线和锁的互斥逻辑,这比单步调试高效得多。

四、 从线程安全和层次化设计的理解

  • 线程安全:不仅是程序不崩溃,更是逻辑的严密。任何一个共享变量的读写,都必须思考当前是否有其他线程正在操作。线程安全的本质在于对并发访问的合理序列化。

  • 层次化设计:这是应对需求变更的利器。因为我将电梯的行为与调度器的分配逻辑严格分层,所以在面对新增“检修”指令和“双轿厢”改造时,电梯线程的代码框架几乎不需要大改,只需要在调度器层面增加回收逻辑,在电梯状态机中插入检修流程即可。

五、 大模型的使用心得

  • 模型名称:Gemini

  • 分工与优势:在这次极其复杂的电梯任务中,我主要让大模型辅助我进行“状态机设计”和“正则表达式/输入解析”的构建。面对诸如检修流程这种包含多个状态流转的需求,大模型能很快帮我搭建出状态模式的骨架代码。同时,在排查死锁概念时,大模型的解释非常通透。

  • 遇到的困难:大模型在处理这种带有极强业务约束的多线程调度时,往往会给出看似正确但实际上暗藏数据竞争的并发代码。我不能直接复制大模型的线程控制代码,必须自己去精细控制 wait/notify 的位置。它是一个极好的架构顾问,但具体的锁机制还需亲自操刀。

六、 二单元真实体验和感受与建议

感受:第二单元绝对是OO课程中“痛并快乐着”的巅峰。看着多部电梯在控制台中按照预期的逻辑井然有序地上下翻飞、完美避让、按时检修,那种成就感是单线程编程无法比拟的。同时,因为多线程Bug的随机性,熬夜看Log查死锁的经历也让人印象深刻。

建议

  1. 希望课程组在下发指导书时,可以提供一个简单的可视化工具或者日志分析脚本的范例。单纯看海量的文本输出,对初次接触多线程的同学来说过于折磨。

  2. 检修状态机中对于乘客“清空与重分配”的边界条件描述可以再详细一些,比如检修结束时,原先被赶下车的乘客是否具有上车的优先权等。

...全文
25 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

309

社区成员

发帖
与我相关
我的任务
社区描述
2026年北航面向对象设计与构造
java 高校
社区管理员
  • 孙琦航
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧