301
社区成员
发帖
与我相关
我的任务
分享当我开始学习关于同步和互斥的概念时,它们似乎是如此复杂和抽象。然而,通过三次作业的实践,我逐渐对这些概念有了更深入的理解。
在前两次作业中,我主要使用了Java中的synchronized同步块。这种同步方式有几个明显的优点。首先,它简单易用,不需要过多的代码和逻辑来实现线程的同步。其次,synchronized同步块内部的代码被锁定,保证了多个线程对共享资源的访问是有序的,从而避免了竞态条件的发生。这种简单而有效的同步方式让我更好地理解了多线程编程中的互斥和同步机制。
然而,在第三次作业中,我面临了一个更加复杂的场景:电梯的换乘层需要进行“加锁”操作,以避免两个电梯在同一楼层冲突。为了实现这一需求,我引入了读写锁的概念。读写锁允许多个线程同时读取共享资源,但在写入操作时会独占锁资源。这种写法的优点在于它提高了并发性能,允许多个线程同时读取楼层信息,而在写入操作时确保了原子性和独占性,避免了数据的不一致性问题。通过这种方式,我成功地解决了电梯换乘层的同步问题,提高了系统的性能和可靠性。
锁在多线程编程中扮演着重要的角色,它象征着同一性和独占性。任何时刻只有一个线程可以拿到锁,这确保了多线程条件下线程的安全性。锁和同步块中语句的关系是紧密相连的,锁定的同步块中的代码或对象使得多个线程在同一时刻只能有一个执行,从而保证了线程之间的互斥性和数据一致性。因此,合理地使用锁和同步块是实现多线程编程中必不可少的技能之一。
(一)架构
一以蔽之,在第五次作业中,我构建了一个Input–Operator–Elevator三级线程管理制度来进行电梯调度与运作。你可以这么想象:有一个上帝一样的电梯操作员(Operator),他实时监测着Input和Elevator的状态是否改变,并通过一本指导书(Strategy)来操作电梯的运行状态。
just like :

具体来说:
当Input输入一个新的PersonRequest时,Operator会为每个Elevator查询Strategy,并给出具体的命令(如Open, Close, Up, Down, Await……)。
当Elevator状态改变时(Arrived, Opened……),Operator会为当前Elevator查询Strategy,并给出具体的命令。
当Input结束且Elevator中无乘客时,Operator下达Stop命令,结束Elevator线程。
我的UML类图如下(其中Elevator和WaitPanel中用于查询的方法非常冗长,这与我实现了三个不同的Look有关):

(二)优点
主流的思想将电梯分为调度和控制两个板块分别完成,这样就导致了控制模块死板僵硬,实现scan的只能跑scan,实现look的只能跑look,为性能优化“留下进步空间”。将所有的控制交给Strategy完成,看似面向过程,但对于某些特定的优化非常简单,如影子电梯,只需在每次查询结果后将对应命令告诉电梯即可(特别是允许自由竞争时)。
(三)缺点
所有的控制都由Operator完成,需要在实现Strategy时将所有细节全部考虑清楚,实现难度大,调试复杂性高。
由于实现的是自由竞争策略,所以Operator在每次下达命令时需要遍历所有电梯,除了处理不好容易死锁之外,时间复杂度也特别高。
代码整体架构死板僵硬,难以添加新的指令及电梯,为后续迭代埋下很大的隐患,不符合OO“高内聚低耦合”的思想。
(一)重构的顶层设计
定性定量对齐顶级玩家颗粒度,深挖流程关键路径,聚焦行业向心力,使Operator在垂直领域实现顶层解耦。
引爆行业内部下沉市场,反哺价值转化新生态,协同赋能推出正交化矩阵,以结果导向沉淀电梯优势方法论。
集成新业态新打法,落地分配控制两大赛道,打出短平快差异化组合拳,发力面向对象快速响应。
具体来说就是:
参考目前主流架构,提炼出较为精炼的思想,使得原本Operator的工作拆分到各个不同的模块中。
改变原有死板的架构,为未来迭代留足空间,形成一个合理完善的电梯架构。
整合网络上已有资源,并结合实际测试卷性能分。👿👿👿
(二)设计新架构的心路历程
在与佬们的交流中,我得知了这么一种近似完美的架构:

(而且据rwg老师统计,绝大多数人第五次作业用的是这个架构,这下遥遥落后了/(ㄒoㄒ)/~~下次作业前我一定好好看钟鼓楼博客~嗯嗯(点头)
它最大的好处在于将“调度策略”和“运行策略”拆分开来,Dispatcher只需要将乘客分配到不同的电梯,再由电梯自身进行运行上的控制。我在第五次作业中一味追求对电梯的统一调度,却忽略了 “‘对电梯进行调度’本质上是‘对乘客进行调度’”这一事实,所以尽管有考虑过相似的设计,最终没有付诸实施。
大纲已经确定,我们能否根据第六次作业的特点改进这个架构呢?
首先,由Dispatcher将乘客直接分配给waitList,我认为是不合理的。理由如下:
Dispatcher的职责是将Input的请求进行分发,所以由Dispatcher决定分发的对象是合理的;而将乘客加入waitList属于对请求的处理,不是Dispatcher的工作(注意这里分发和处理的区别)。
(最重要理由)我认为,由谁将乘客插入waitList是很显然的——谁初始化了Elevator,谁操作waitList。那么Elevator应该在哪里初始化呢?不是Main,不是Input,更不是Dispatcher,而应该是一个专门的类。
迭代后的请求很可能不止一种,如本次作业的Reset,就不适合通过Dispatcher直接分发。未来可能会有的增加、删除电梯命令更是如此。
经过以上分析,可以得出结论:需要添加Controller类,用于初始化电梯和对电梯的属性进行操作,Controller需要接受Dispatcher的命令,对请求进行处理。
其次,我认为需要添加Elevator的观察者类(Observer)。
灵感来源于有位佬凌晨三点问我的一个问题:你是如何将被Reset的电梯中乘客重新分配给这部电梯的?
他的问题可以被进一步概括为:Dispatcher如何确定乘客分发的电梯?
一个最显然的策略是“将乘客分配给 能够将当前已分配的所有人 性能分最高地运送到目的地 的电梯”,但如果直接读取电梯的状态和队列,容易造成线程不安全的情况,也不够优雅。如果碰到电梯正好都在Reset,还需要循环等待电梯Reset结束(如这篇文章前面讲得那样orz)。不妨添加一个Observer类,和电梯构成观察者模式,电梯每状态改变时,便通知Observer存储当前状态,从而将电梯从动态转化为静态,便于各种操作的实施。
最后,既然已有的类名称都是*er,不妨将Input改名Monitor(中二地笑hhh)。
综上,我的第六第七次作业架构如下:

最终UML类图长这样:

总结来看,我的电梯系统架构中实现了一个读写锁,以确保电梯在进入换乘楼层时能够获取写锁并进行锁定,离开时再进行解锁操作。这种设计使得电梯系统在换乘楼层时能够安全地处理并避免冲突,保证了系统的稳定性和可靠性。
在我的架构中,我将电梯的运行策略和换乘操作分开考虑,这样电梯可以将重心完全放在如何更快地运行上。为了优化电梯的运行速度,我采用了look算法,让两部电梯分别进行运行命令的查询,从而提高了系统的响应速度和运行效率。
相比之下,我注意到一些同学将到达楼层和离开楼层视为一个原子操作,我认为这种做法是不合适的。因为将这两个操作合为一个原子操作可能会引发竞争条件、数据不一致性和死锁风险等问题,影响系统的稳定性和可靠性。因此,我坚持采用读写锁的方式,将电梯的到达和离开操作分开处理,以确保系统能够正常运行且保持良好的性能。
多线程的bug,那能叫bug嘛…那叫优化空间。
老实说,我几乎所有bug都是老老实实画图de出来了,很少有看到错误信息就知道是哪部分错误的情况。
我的习惯是拿出一张白纸,将所有可能存在线程竞争的资源细致地分门别类地写下来,包括这些资源的依赖关系以及是否需要同步写等重要信息。接着,我会对照代码,仔细检查这些资源在项目中是否都被正确实现了功能。这个过程不仅有助于我发现潜在的竞态条件和同步问题,也使得我对整个系统的结构和运行机制有了更深入的理解。
此外,也可以使用idea的一些工具辅助查看线程状态。在关键代码段或者线程执行的关键点添加日志记录,记录线程的状态、执行路径、关键变量的值等信息。通过查看日志可以更好地理解程序的执行流程,帮助定位问题所在,特别是在第七次作业中,日志记录对于跟踪多个线程之间的交互非常有帮助。
最后,也可以和舍友交流代码,互相debug。通过互相审查代码,可以发现潜在的竞态条件、不正确的同步操作等问题。同时,与朋友讨论多线程的实现细节和可能的问题,可以获得更多的思路和建议。
在同步块的设置和锁的选择方面,我学到了不同同步机制的优缺点以及适用场景。对于简单的线程同步,synchronized同步块是一个简单而有效的选择,能够保证线程安全性和数据一致性。但在复杂的场景下,例如多个线程同时读取共享资源的情况,我学会了使用读写锁来提高并发性能和效率。
在调度器设计方面,我逐步深入了解了调度器与程序中线程的交互关系。通过合理设计调度策略和架构,我实现了电梯系统的稳定运行和性能优化。特别是在第六和第七次作业中,通过重新架构调度器和添加新的类,我实现了更加灵活和高效的电梯调度系统。
关于程序bug的处理和debug方法,我发现多线程编程中的bug可能比单线程更加难以发现和解决。通过画图分析可能存在的竞态条件和同步问题,结合日志记录和调试工具,我逐步提高了对多线程程序bug的处理能力。同时,和同学交流和代码审查也是非常有益的,可以发现问题、提出建议并共同解决bug。
在OO单元的三次作业中,我不仅学到了多线程编程的基本概念和技术,还锻炼了分析和解决问题的能力,提高了系统设计和优化的水平。这些经验和心得将对我未来的软件开发工作和学习有着重要的启发和指导作用。
总之,尽兴就好啦~!(oo赛高!)