301
社区成员
发帖
与我相关
我的任务
分享1. 同步块和锁的设置及选择分析
三次作业中都主要使用了 synchronized 关键字来实现同步块和锁,保证了对关键部分的互斥访问,以确保线程安全性和避免竞态条件。以下是代码中的同步块和锁的分析:
synchronized 方法:
在 stateExchange、haveMainRequest、fetchMainRequest、updateMainRequest、someoneArrived、someoneOutside、pickup、getAway、pleaseComeIn、pleaseGoOut、takeAction 等方法中,你使用了 synchronized 关键字修饰方法,确保了在调用这些方法时,只能有一个线程能够进入方法内部执行。这些方法对关键部分的操作进行了同步,避免了多线程并发执行时的竞态条件,保证了数据的一致性和正确性。
synchronized 块:
在 pleaseGoOut 方法中,你使用了 synchronized 块来对多个操作进行同步,保证了多个操作的原子性。例如,在 while 循环中的多个条件判断和状态转换操作,通过 synchronized 块确保了这些操作的连续性,避免了在多线程环境下可能出现的数据不一致或错误状态。
对象锁:
你使用了对象锁来保护共享资源,例如在对 insideElevator 和 outsideElevator 进行操作时,通过 synchronized 方法或 synchronized 块来获取对象锁,确保了对这些共享资源的互斥访问,从而避免了多线程下的数据竞争和不一致性。
锁的粒度:
你在方法级别和代码块级别都使用了 synchronized 关键字,这种粗粒度的锁可能会导致程序性能下降。合理的锁粒度可以提高并发性能,你可以考虑根据具体情况细化锁的粒度,只在需要同步的关键部分加锁,减小锁的范围,以提高并发度。
2. 调度器设计分析
电梯线程的启动:
当有乘客请求到来时,主线程创建一个新的电梯线程,并将该乘客请求分配给相应的电梯。电梯线程通过 Thread.start() 方法启动电梯线程,开始执行电梯的运行逻辑。等到确认电梯内外等待队列都为空时,关掉线程。
电梯线程的执行:
电梯线程在运行过程中,通过不断地循环检查外部乘客请求和内部乘客请求,根据当前电梯状态和乘客请求情况,决定电梯的运行方向和行为。
调度方面,因为在hw5互测的时候,同屋的同学针对我的接送策略给出了让我的电梯上下震荡的极端数据导致超时,到最后我也没有成功修复这个bug。使我在hw6的时候直接在分配策略上选择了random,无论输入怎么样,都random一个电梯号数,然后检查电梯是否在reset,如果是就再random一次。
3. 架构设计逐步变化与未来扩展能力
这是第三次作业的架构
初始设计包括 Elevator、Passenger 和 MainClass 等类。
Elevator 类实现了大部分电梯的运行逻辑,以及电梯的运行状态机,包括状态管理、乘客上下、楼层移动等功能。
Passenger 类表示乘客,存储乘客请求信息,并管理乘客的状态。
功能扩展:
增加了TransferFloor、Building类,TransferFloor 类表示楼层,用于处理双梯运行时的换乘楼层。Building 类用于管理多部电梯。
在初始设计的基础上,添加了双梯运行的支持,通过DoubleCarResetRequest 类和相关逻辑实现了双梯的重置和运行。
添加了乘客请求的处理,包括人员的上下乘坐和状态更新。
实现了电梯的状态管理,通过 State 枚举类来表示电梯的不同状态,从而控制电梯的运行逻辑。
https://s21.ax1x.com/2024/04/19/pkSt2kt.png
4. 线程之间的协作关系分析
我的线程之间没有协作关系,每个电梯是相互独立的线程,然后一个线程负责接受输入并分配电梯,仅此而已了。
5. 稳定内容和易变内容分析
稳定的内容应该就是整体架构以及电梯的运行逻辑。两次作业的迭代中对于新增加的输入指令,都是基本上复制的第一次作业的指令稍加修改完成的。
hw5的输入
https://s21.ax1x.com/2024/04/19/pkSthp8.png
hw6新增的输入
https://s21.ax1x.com/2024/04/19/pkStTmj.png
在状态机之外包裹了一层大状态,分别是确定电梯是否运行的running,以及两种reset状态。
还有一些比如系统的基本逻辑,如电梯的运行状态管理、乘客的请求处理等,是系统的核心功能,不太容易变化。系统中使用的基本数据结构,如队列、列表等,也属于稳定内容,因为它们是支撑系统运行的基础。
https://s21.ax1x.com/2024/04/19/pkSt70s.png
核心算法和调度策略:如乘客分配到电梯的规则、电梯的运行路径计算等,通常是相对稳定的。还有主要类和接口:系统中的主要类和接口,如电梯类、乘客类、楼层类等,一般是稳定的,因为它们定义了系统的基本结构和功能。
而易变内容...每次作业可能需要添加新的功能或扩展现有功能,比如乘客的换乘、重新分配等。这些功能的添加属于易变内容。
6. 实现双轿厢不碰撞的方法分析
1.写一个换乘层类,其拥有一个布尔属性。
2.当某个电梯需要经过换乘层时,变更布尔属性,在动作结束后主动离开换乘层,并将属性变回未占用。
https://s21.ax1x.com/2024/04/19/pkStH7n.png
3.在两个电梯都需要用到换乘层时,晚来的电梯等换乘层布尔属性变为未占用时再进入。
https://s21.ax1x.com/2024/04/19/pkStqkq.png
但实际上就是轮询了。我承认...
7. 多线程程序的 bug 分析与 debug 方法
纵观三次作业,我感觉问题最大的还是我的状态机以及捎带逻辑。因为判断并接主请求的运行逻辑和电梯的运行逻辑并不是同一个东西,以至于在我修改状态机之后,必须也对接主请求的过程进行同样的修改。
hw5并没有出现什么严重的bug,唯一为难的就是互测时的超时问题。其实就是捎带逻辑写的不够高效。hw6我进行了状态机的重构。导致了会出现电梯突然一直向上突破天际或一直向下直达地狱的情况。再给所有移动状态加上楼层限制之后,电梯开始在电梯井里打转...经过检查之后发现是接主请求逻辑的问题,在换乘或是接受reset请求之后没有对原来电梯进行主请求重置的操作。hw7中,强测出的最严重的问题是运行超时,具体是什么原因还没修出来。
找bug暂时还没有很好的方法,都是用眼睛硬看。至于debug的方法,其一是System.out,另一个就是在输出中就是找到出问题的电梯,将random设为1,再次输入同样的数据。这样就只有一个线程,更好操作。
8. 心得体会
线程安全的重要性:
同步机制的应用:合理使用锁和同步机制可以确保多个线程在访问共享资源时不会产生竞争条件,从而避免数据不一致或死锁等问题。
原子操作的使用:借助原子操作和线程安全的数据结构,可以简化并发编程的复杂性,并降低出错的可能性。
共享资源的保护:对于共享资源,采用适当的访问控制和保护机制,确保多个线程对其访问的顺序和方式是安全的。
层次化设计的优势:
模块化和分工:将系统按照功能模块进行划分,每个模块负责不同的功能,有利于团队分工合作,提高开发效率。
降低耦合性:层次化设计使得不同模块之间的依赖关系更清晰,降低了模块之间的耦合度,便于后续的维护和扩展。
易于调试和测试:模块化设计使得单元测试和调试更加容易,可以快速定位和解决问题,提高了代码的质量和稳定性。
面对挑战的经验和收获:
理解多线程编程:通过本单元的作业,我更深入地理解了多线程编程的复杂性和挑战性,学会了如何处理并发情况下的各种问题。
掌握同步机制:学会了如何正确地使用锁、原子操作和线程安全的数据结构来保证程序的线程安全性,防止出现数据竞争和不一致性。
提高设计能力:通过层次化设计,我学会了如何将一个复杂的系统分解为多个简单的模块,提高了自己的系统设计能力和组织能力。
优化程序设计和调试过程:
性能优化:在保证程序正确性的前提下,我学会了如何通过合理的算法选择和数据结构设计来优化程序的性能,提高程序的执行效率。
调试技巧:通过多次调试和测试,我积累了丰富的调试经验和技巧,学会了如何快速定位和解决问题,提高了代码的可靠性和稳定性。
持续学习:多线程编程是一个复杂而深奥的领域,我意识到自己还有很多需要学习和提高的地方,会持续不断地学习和探索。