302
社区成员




在本单元的学习中,我深入探索了多线程编程的多个方面,包括同步块的设置、锁的选择、调度器设计、调度策略,以及线程协同架构模式。通过三次作业的实践,我对这些概念有了更深刻的理解,并在实践中获得了宝贵的经验。
在三次作业中,我学习了如何根据不同场景合理设置同步块和选择锁机制。同步块保证了在多线程环境下,共享资源的访问不会发生冲突。我分析了同步块中的处理语句,确保了它们与锁机制之间的协调一致,以避免死锁和资源争用。
三次作业中,我在刚刚接触多线程编程时先采用了较为粗粒度但是较为简便的同步块( synchronized
语句块进行同步控制)。后面改用对 todo 加锁。以后再写多线程程序一定试试用更细粒度的锁或者线程池等提高效率,这次作业的时间相对仓促就没有尝试。
此外,比较好的方法是将请求等待队列设置为线程安全类,这样为电梯分派请求和在电梯维护时放回请求等操作都不需要再设置同步块或同步方法。此外,Java 自带的线程安全类的执行效率比手动编写同步块更高。
调度器是多线程程序的心脏,它决定了线程的执行顺序和时间。我在第一次作业中采用了自由竞争的摆烂设计(),后面被 RECEIVE
等强行 ban 以后采用了调度器线程和全局等待队列交互,全局等待队列在电梯、调度器等等线程之间共享。
秉持着“大道至简”的方法,尝试了若干种性能方法,但是发现性能的提升小如睚眦,即采用复杂的调度策略并不能取得过分优异的成绩,故我采取了 %6 的朴实策略。
为了更直观地展示线程之间的协作关系,我绘制了 UML协作图,详细展示了包括主线程在内的所有线程的协同工作和消息传递。
稳定的内容是程序的核心,而易变的内容是程序的扩展,用于适应程序在不同场景不同需求下的变化。
稳定
架构稳定
总的流水线架构和思路是确定的,并不随着具体的场景变化而发生变化,变化都发生在具体的线程内部
数据结构稳定:支持并发读写的请求表数据结构是这三次作业的核心内容,且保持了前后的一致
变化
场景变化
具体的场景需求是随着用户需要而改变的,会影响电梯的参数、调度策略等等
双轿厢设计的核心是两个轿厢不能同时进入换乘楼层,而其实现分为信号量方法和将换乘楼层划分的方法。
信号量方法
很自然可以想到将换乘楼层设为临界资源的想法,采用信号量将换乘楼层的访问设置为对临界区的访问,只有取得信号量的轿厢才能进入。
换乘楼层划分方法
在接收到 DoubleCarResetRequest
时,将换乘楼层划分给其中一个区域,让两个轿厢分别运行在这两段无交集的区域内。具体的实现方法包括但不限于按照奇偶性划分等。这种方法可以避免所有电梯同时接收 DoubleCarResetRequest
且换乘楼层相同时造成的“断流”现象。
本次作业在第二次作业本地测试时,
判断线程终止条件时,删去了原有的请求队列为空的条件判断,导致轮询,ctle
此外还出现了电梯因维护而停止之前停靠楼层过多的问题,提前对 isMaintained
的判断,解决了该问题
多线程编程是现代计算机软件提升运行效率最重要的手段之一,性能和编码难度是很难兼顾的,想数倍提升软件的 performance,多线程的硬骨头必须啃下来。
小马过河。本单元最大的问题就像是小马过河。你会在六系的各大论坛听到对多线程电梯单元难度的讨论。但经过对多线程的系统学习(也不算系统,听了几个小时的培训)和对代码原理的揣摩,感觉难度尚可,没有想象中的不可触摸不可逾越。也许迎难而上然后发现不难,是oo这门课的精神和风骨。
多做回归测试。第二次作业的失利原因就在于,更改了线程的终止条件后,没有做恰当的回归测试,导致浪费了不必要的时间。