OO面向对象编程 U2博客

吉俊西-24373190 2026-04-25 00:40:31

OO U2博客

第二单元围绕电梯调度考察多线程编程。

  • 第一次作业涉及wait-notify模型,生产者-消费者模型
  • 第二次作业新增检修请求,要求妥善处理检修相关变量,避免并发安全问题
  • 第三次作业新增双轿厢调度,要求对一个电梯井做层次化设计

同步块和锁

同步块的设置

同步块应该力求精简,其设置的粒度越细,程序的并发效率也就越高。

有的时候可以舍弃一些绝对安全的写法,来进一步提高性能。

例如,第三次作业中,对于一部电梯是否进入了双轿厢状态,我设置了一个doubleFlag变量来做标识;在电梯让位的时候需要判断doubleFlag是否为真,如果为真才进行让位。

看完上述表述你会发现,在电梯让位过程,从判断到改变电梯状态,最后到输出ARRIVE,这一整套下来doubleFlag是不能被外部写入的,不然可能会出现电梯已经变回单轿厢状态后还进行让位的情况。所以我们需要对doubleFlag上锁————但是真的有必要吗?

可以预见的是,这个并发安全只是由两个线程之间的RAW读后写问题引发的,一是涉及的线程数目少,二是出现这个情景的机会并不多;我们只需要在出现问题之后进行一定的补救措施,把电梯的状态改回来即可。这就好比一个CPU执行分支指令的时候,不可能让分支指令后面的指令阻塞到分支指令出结果才执行,而是在分支预测失败之后,对流水线进行冲刷即可,无事发生。

锁的选择

大部分地方用对象锁就可以。对于双轿厢模式下二楼的管理,我让电梯进入的时候获取对于二楼的锁f2Monitor,离开二楼的时候释放即可;这个实现使用了java内置的Lock对象。

锁和同步块中语句的关系

同步块中的语句应尽可能减少对其他锁的获取,不然调试死锁问题会比较苦恼。

调度器设计

汗。。。我的调度策略就对每个电梯均匀分配的。

调度器和其他线程的交互

整体的生产者-消费者模式类似:

InputThread --RequestQueue--> DispatcherThread --ProcessingQueue--> ElevatorThread

调度器DispatcherThread从请求队列获取任务,然后分发给各个电梯的处理队列中。

从输入线程InputThread的结束开始,各个线程逐级结束。调度器DispatcherThread接受到InputThread的结束信号,如果满足一定条件,则对电梯线程发送结束信号。

调度策略

平均分配的好处是性能差不到哪里去,对于正在进行检修、升级和回收的电梯,他们最多7s不能处理乘客请求,不会出现一直处理不了导致最终超时的情况;所以不需要对这几个状态进行特判,无脑平均即可。

第二次作业有人做了对于检修的特判,然后被奇妙数据点hack了。这个数据点让五个电梯全部检修,然后塞50个请求,于是这50个请求都跑到了同一个电梯里面,最后超时了。

好像最卷的策略是影子电梯,搞一个模拟器看哪个电梯完成它自己的任务需要的时间最短。但是我摆了。

bug以及debug

bug调试好像没有想说的,面对并发问题还就是那个print大法。轮询的时候,终端出现一串输出;出现并发问题的时候,检查输出出的相关变量。

线程安全与层次化设计

从第二次作业我开始思考层次化设计。具体情景如下:

情景一

第一次作业的时候,我的架构类似:

InputThread --RequestQueue--> DispatcherThread --> ElevatorThread

结束的时候,从InputThread开始顺序结束,非常自然。

不过,第二次作业新增了一个要求:维修的时候,电梯需要把所有的乘客回流到前面的层级,进行重新处理。上面的架构就略显奇怪了——具体应该回流到哪里呢?好像只有RequestQueue满足要求。但这个方案并不理想,原有的层级就全都被破坏了。

于是乎,在DispatcherThread和ElevatorThread加上一个缓存,这样就解决了!所有的回流请求都可以被回流到这个缓存里,如下:

InputThread --RequestQueue--> DispatcherThread --ProcessingQueue--> ElevatorThread

情景二

如何优雅地结束呢?我的方案是维护一个全局变量requestCount,来表示还有多少剩余任务。那么这个变量应该存在哪里,如何处理并发问题呢?

建立一个全局单例类存储requestCount,并为其创建原子的settergetter方法。在多线程的情境下,这个变量的修改并不会被及时写回主存,被其他线程读取到最新的值;所以为其添加volatile关键字。

情景三

如何处理换乘层的撞车问题呢?需要一个高于电梯层级的类来管理主副轿厢的通信。于是增加一个电梯井的层级,在其中管理换乘层的锁f2Monitor。电梯进入换乘层之前,向电梯井申请f2Monitor;离开换乘层的时候释放f2Monitor

锁不应该被暴露给外部,应该利用锁去封装public方法。具体到这个例子中,电梯不应该直接持有f2Monitor的引用,而是应该调用电梯井中类似intoF2()leaveF2()的方法。

大模型使用心得

我个人对大模型的使用还是相对保守的()。听说现在大家慢慢在搞工程化、可验证的ai编程,而非简单的提示词工程了;我觉得我对ai的使用能力还不过关,所以在作业中,如果时间允许的话,我还是倾向于把ai当做一个知识顾问。

比较有趣的是,前两次作业,因为我的个人原因,基本都是vibe coding的。最后强测很惨。第三次作业的时候重构了所有的东西,一次性写了三次作业的内容。。。最后进a房了,也算有一个比较好的结果吧。

U2体验和感受

被多线程打蒙了,不过感觉这次第二单元还是比往年要简单了点。。。大家一起挑战一个难题是一件很有意思的事情,受益匪浅。感觉指导书和测试体验都不错,希望助教组再接再厉吧。

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

309

社区成员

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

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