2026_OO第二单元博客

何劲杉-24373530 2026-04-29 23:24:02

OO第二单元博客

一、锁与同步块

在多线程电梯作业里,同步操作主要集中在输入队列、请求队列这类共享数据结构上。这些队列会被多个线程同时访问,因此必须通过加锁保证读写操作的互斥,避免数据竞争与状态不一致。

后续作业中,我针对不同的等待场景分别设计了独立的等待对象,并基于这些对象实现等待与唤醒机制。这种做法可以让线程只等待自身相关的条件,避免因无关通知被唤醒而造成无效操作。

在最终作业中,涉及线程等待的场景主要包括:当六部电梯均无法接收请求时,调度线程需要等待;回收流程中,主轿厢需要等待备用轿厢相关流程完成才能恢复正常运行;双轿厢模式下,若其中一个轿厢需要使用 F2 层但该楼层被占用,需等待另一轿厢让出位置;程序结束时,需等待六部电梯全部进入无任务空闲状态,再统一终止所有线程。

对于上述每一种等待条件,我都设置了对应的条件变量。线程在等待时会先获取对应条件变量的锁并进入等待;当条件满足时,相关线程获取同一把锁,再唤醒等待在该条件上的线程,从而保证各类等待逻辑独立、准确且高效。

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

在第一次作业中,每个乘客请求已经指定了目标电梯,调度器只需要按照指定结果,将请求直接放入对应电梯的请求队列中即可,不需要进行额外决策。

第二次作业改为由调度器动态分配电梯。dispatcher类调度器收到乘客请求后,将请求交给电梯管理者类,管理者根据每部电梯的实时状态计算评分,选择评分最优的一部电梯进行分配。

这是非常经典的评分策略,在该策略的实现中,需要频繁读取其他线程信息,需要注意并发问题,仔细加锁保护。

评分主要依据以下策略:优先分配给当前运行方向相同、可以顺路载客且未超重的电梯;优先选择距离乘客出发点更近、运行路线与请求路线重叠度更高的电梯;尽量避免将请求分配给当前负载较高、队列较长的电梯。

按照这套策略,系统可以优先利用顺路电梯完成载客,减少不必要的调度;对于无法顺路的请求,能够及时分配到其他电梯,避免乘客等待过久;同时让路线重叠的请求尽可能由同一部电梯承载,提高电梯运行效率。对于暂时无法判断最优分配的请求,不会立即指定电梯,而是等待电梯完成当前运行周期后再重新决策。电梯管理者内部对每部电梯采用 LOOK 算法进行运行控制。

三、BUG与DEBUG

第三次作业中,我遇到了和结束条件判断相关的 BUG:输入已经读取完毕,但电梯仍有未完成的请求时,程序就提前终止了电梯线程。

我的初始设计是维护一个统计变量,记录当前处于空闲等待状态的电梯数量。电梯进入等待时对该变量加一,被唤醒后减一。原本的结束逻辑是:当输入结束且请求队列为空时,只要空闲电梯数达到 6,就终止调度线程与全部电梯线程。

该问题在本地难以复现。排查后发现,根本原因是:请求已经从输入队列取出、进入分配流程,但尚未绑定到具体电梯的极短时间窗口内,输入队列显示为空,且六部电梯都处于等待,系统会误判所有任务已完成,从而错误终止程序。由于这个时间窗口极小,直接调试很难复现,我在调试时让分配器线程 sleep (1000) 拉长该窗口,成功稳定复现并定位了问题。

我对修复方案做了优化,不再只依赖队列状态与空闲电梯数量,而是采用更可靠的请求计数机制:维护两个全局变量,分别统计总请求数与已完成请求数。当输入结束、所有请求均已派发完成,并且已完成请求数等于总请求数时,才判定程序可以正常结束。这种方式不受中间分配状态影响,能准确识别所有请求是否真正处理完毕。加入这套计数判断后,结束逻辑不再误触发,问题彻底修复。

Debug的过程中,我总结了经验教训,但也遇到了许多困惑。

需要了解相关位置错误信息时,可以使用System.out.println()语句输出错误信息。例如:System.out.println("Error: " + e.getMessage());,此外还可以输出线程ID、异常类型等信息,有助于更好地定位问题,掌握情况。

然而,一些线程同步的相关问题会在使用println()语句后无法捕捉到。该现象推测是因为println()语句会引入额外的同步,导致线程同步问题的消失。这带来了较大麻烦,需要在Debug时注意。

四、线程安全和层次化设计

在多线程电梯作业中,线程安全和系统架构设计是保证程序稳定运行的关键。同步操作主要围绕共享资源的访问控制展开,输入队列、请求分配等核心环节需做好数据保护,避免出现数据错乱或逻辑异常。

多线程环境下,共享数据的安全访问是重点,输入队列、请求分配等共享对象需通过加锁实现互斥访问,防止多个线程同时操作导致的数据冲突。针对不同的等待场景,需结合实际业务需求设计专属的同步机制,而非统一采用单一锁机制,确保每种场景下的线程操作都能有序进行。

调度器与电梯的状态管理需分离,调度器负责请求分配决策,电梯负责自身运行状态的维护,两者通过规范的接口交互,避免直接操作对方的核心数据。在设计时,需充分考虑不同场景下的线程协作,比如请求分配、楼层占用、维修流程等,均需单独设计同步逻辑,确保各环节互不干扰。

程序结束阶段的判断的核心是确保所有请求都已处理完毕,而非单纯依据电梯状态或请求队列是否为空。通过统计请求总数、已完成请求数,结合电梯运行状态,才能准确判断是否可以终止所有线程,避免因提前终止导致的请求遗漏或数据异常。

分层设计能有效降低程序复杂度,将输入处理、请求调度、电梯控制、状态维护等功能拆分到不同模块,每个模块独立实现自身功能,模块间通过明确的接口通信,既便于单独调试,也能减少某一模块故障对整体系统的影响。同时,合理的分层的设计也能减少线程间的交叉干扰,让同步控制更精准,进一步提升系统的稳定性和可维护性。

五、大模型使用心得

思路构建辅助:面对复杂的多线程逻辑与项目开发,大模型可以帮忙梳理整体思路,快速搭建基础代码框架,理清模块之间的关联与实现逻辑,避免一开始就陷入逻辑混乱。

代码简单审查:面对编写中出现的语法问题、逻辑漏洞,ai能对代码进行简单审查,找出隐藏的细节错误、不规范写法以及潜在的线程安全问题。不过这方面不能寄予太大希望,ai模型的审查能力有限,只能发现一些明显的错误,不能完全替代开发者的经验。

评测机开发:ai可以协助构造评测机。尽管这个过程比较曲折,经历了一番鏖战,并且第二次到第三次作业的迭代过程中增添了额外的负担,并且也没能找出所有bug,我仍然认为这个方向是可以继续发展强化的,能带来可以预见的巨大收益。​​​​​​​

知识咨询:在项目开发过程中,我遇到了一些与多线程安全相关的问题,大模型能够帮助我理解问题的根源,提供一些解决方案。

然而,大模型很难完全替代开发者的思考和判断。在处理复杂并发逻辑时,大模型的理解能力非常有限,它经常会给出看似正确但实际上存在严重线程安全问题的代码。在并发性问题上,不能过多依赖大模型的建议,而要结合实际业务需求和经验进行判断。

六、体验和建议

我认为第二单元情景化的电梯任务是非常有趣的,但是我在该单元的测试中所取得的成绩比较惨淡。究其原因,是没有养成多线程安全的意识,就一头撞进了开发中,甚至不理解“线程安全容器”作用的效能与边界。

此外是作业本身,输出难以评测正确性,难以debug了解问题所在。多种多样的策略选择,多线程的随机性,注定了该作业拉锯式的复杂性和挑战性。

总的来说,该任务在着力考察多线程问题理解,促进相关知识学习运用的成效是显著的。因此我依然十分乐于见到这样的任务,只是希望在评测方面课程组能提供更多的帮助,便于debug。毕竟,学习相关知识才是问题设计导向的核心意图。

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

304

社区成员

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

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