BUAA OO第二单元博客总结

第二单元博客总结
一、总结分析三次作业中同步块的设置和锁的选择,并分析锁与同步块中处理语句之间的关系。

三次作业中的架构基本不变,即数据流方向为:

输入线程Input_Thread >> 调度器线程Schedule >> 具体电梯线程Process

其中输入线程Input_Thread 和调度器线程Schedule共享一个Request_Queue类对象wait_Queue,将wait_Queue的各种修改(如添加需求、读取需求、取出需求等)的具体实现方法前均添加synchronized关键字,从而实现对wait_Queue的细粒度操作。同时,在必要的修改类代码块末尾加上NotifyAll()实现释放锁的同时提醒wait_Queue对象锁的等待队列中的进程可以尝试获取锁。

同理,调度器线程Schedule和具体电梯线程Process也共享一个Request_Queue类对象processing_Queue,对其的同步块的设置同上。由此可见,在代码具体实现中只需要实现一个Request_Queue类,通过实例化对象就可以产生各个线程间的共享对象并实现上锁。

就锁与同步块中处理语句之间的关系而言,运行同步块中的处理语句的线程必须要先获取同步块指定对象的锁,并且在同步块运行结束后释放掉该指定对象的锁,从而实现一次只有一个线程能够修改或读取共享对象中的内容,避免了不同线程修改后读取结果不确定的问题。

二、总结分析三次作业中的调度器设计,并分析调度器如何与程序中的线程进行交互;总结分析三次作业中的调度策略,并分析自己的调度策略是如何适应时间、电量等多个性能指标的。

第一次作业调度使用纯random调度,第二、三次作业都将random嵌入评分系统进行综合评分后调度。

关于调度器如何与程序中的线程进行交互,我是通过输入线程Input_Thread 和调度器线程Schedule共享一个对象wait_Queue,调度器线程Schedule和具体电梯线程Process共享一个对象processing_Queue,从而实现了调度器线程与程序中的输入线程、实现线程之间的交互。

总结分析三次作业中的调度策略,三次作业调度策略的迭代过程实际上是逐步增加要考虑信息的深度和广度的过程:

第一次作业中使用了Random调度,但实际上在Random之前会进行一个判定,如果电梯的运行方向和待分配需求方向相同,就会直接将该需求分配给该电梯而跳过Random过程。但实际上这样只用了一个电梯指标(方向),且可能会造成只有一个最慢的电梯在运行的情况,缺陷明显。

第二次作业中由于不再指定具体需要运行的电梯,所以调度情况变得更加复杂。我采用评分系统,对于速度方向,用(1-time)*10作为速度评分;容量方面将(capacity - loaded_person_Num)*10作为余量评分。对正在重置的电梯实行一票否决,将其评分设为-1,此时可能产生同一时刻的大量需求堆积到一个未在重置电梯上的性能问题,这里引入Receive_Num指标,若电梯Receive_Num >= 2*capacity,就会停止从待分配队列中获取需求。此外,还考虑了电梯运行方向、电梯所在楼层和目标楼层的距离等指标,这样一来就可以求出所需时间、电量最少的情况。第三次作业在第二次作业的基础上扩展了一票否决功能,将运行范围内无法到达需求起始楼层、或只能到达需求起始楼层的电梯也一票否决。

在第二、三次作业中,评分后会取出评分最高的电梯,若最高评分为-1,则没有任何一个电梯接收调度器中的需求,那么该需求就会阻塞在wait_Queue队列中,直到有电梯线程或输入线程发生变化,再来尝试调度。

三、结合线程协同的架构模式(如流水线架构),分析和总结自己
(1)三次作业架构设计的逐步变化和未来扩展能力画UML类图。
第一次作业UML类图如下:

第二次作业UML类图如下:

最终作业的UML类图如下:

由上面三个UML类图可知:迭代开发中代码的多线程处理架构是不变的,变的只是实现处理的具体方法。在进行未来的扩展时,仍然能够使用代码的多线程处理架构的接口而只改变具体的方法实现,这展现了代码优良的可维护性。

(2)画UML协作图(sequence diagram)来展示线程之间的协作关系(别忘记主线程)

线程总共有Input_Thread、Schedule、Elevator、Main四种线程。

由于三次迭代中线程总体架构不变,因此这里只画最后一次的UML协作图如下:

(3)识别出三次作业稳定的内容和易变的内容,并加以分析

三次作业稳定的内容是代码的实现层次和信息流动方向,易变的是信息获取(Input_Thread)、信息处理(Schedule)和实现(Process)的具体方式。因为作业一开始的层次结构设计得比较开放,因此在后续的代码迭代中不需要对代码的实现框架进行大改,只需要微调一些层次中代码的具体实现方式。这体现了代码具有高度可维护性和开放性的特点。

四、分析自己在第三次作业中是如何实现双轿厢的两个轿厢不碰撞的。

使用类occupy作为相对应的A-B电梯对的共享对象,occupy类负责维护一个IntegerOccupy_State,其初始值为0,表示电梯交换楼层无人占据。将其修改函数和读取函数放入occupy类的临界区并且保证只有这两个方法能够访问和修改Occupy_State对象。 ​ 当A电梯运行至transfer_floor - 1且运行方向向上时,若Occupy_State0,则修改其为1,表示A电梯占据或将要占据电梯交换楼层,反之若Occupy_State2,则A电梯会等待在occupy对象上并且等待occupy对象的notify。当A电梯运行至transfer_floor - 1且运行方向向下时,若Occupy_State1,则修改其值为0,并notify挂在该对象上的所有线程。 ​ 当B电梯运行至transfer_floor + 1且运行方向向下时,若Occupy_State0,则修改其为2,表示B电梯占据或将要占据电梯交换楼层,反之若Occupy_State1,则B电梯会等待在occupy对象上并且等待occupy对象的notify。当A电梯运行至transfer_floor + 1且运行方向向上时,若Occupy_State2,则修改其值为0,并notify挂在该对象上的所有线程。 ​ 综上所述:通过在可能碰撞的A、B轿厢电梯线程中设置共享对象Occupy保证线程的实时通讯,实现了A,B轿厢线程间的同步互斥,从而避免两个轿厢碰撞。

五、分析自己程序出现过的bug以及自己面对多线程程序的debug方法。

(1)轮询问题 使用助教们提供的Debug方法是卓有成效的。首先,轮询一般发生在while(true)语句块中。首先在每个while(true)语句块结束前输出一个标志信息,一般来说没有发生轮询的标志信息数还大致可数,而一旦发生轮询则将产生几百万条的标志信息,错误线程是显而易见的。更精切的debug需要给轮询while(true)语句块中的每一个条件语句添加输出标志信息,就可以明显地看出是哪个条件语句在需要wait()时没有wait()。

(2)死锁问题 官方推荐的JProfiler插件较为复杂且可能产生错误的分析结果,我选择使用JAVA自带的"jps"指令获取当前运行的JAVA进程号,并通过"jstack"+进程号来查看JAVA进程中正在运行的线程状况,从而判断是什么线程导致电梯进程无法正常结束。

(3)逻辑问题

这一问题是发生最多且最不好debug的问题,尤其是使用Random策略的电梯调度方案,我通过使用dpo生成大量测试用例,通过记录错误输出和报错信息,实现在测试点的单个运行实例上分析出产生错误的代码原因并修改。但是运行实例往往难以复现,或者复现后我们并不知道其已经复现(因为没出问题),所以往往需要继续运行多组dpo数据才能勉强确定是否已经修复问题。如果多个逻辑问题同时出现将会使情况更加复杂。所以我的结论是不到万不得已不要用Random调度算法,否则会给debug带来巨大的不便。

六、心得体会。从线程安全和层次化设计两个方面来梳理自己在本单元三次作业中获得的心得体会。

1.从线程安全方面:

在多线程环境中,多个线程可能同时访问和修改共享资源,如变量、数据结构或文件。如果代码不是线程安全的,可能会导致数据不一致、程序崩溃或其他严重问题。线程安全的代码通过同步机制确保各个线程能够正常且正确地执行,避免数据污染等意外情况。这通常涉及到对共享资源的访问控制,以确保在任何时候只有一个线程能够修改资源,从而防止数据竞争和其他并发问题。

在本单元作业中,必要时要在共享对象中加锁,在一个线程wait()时必须要有线程能够及时唤醒它,否则就需要在wait()括号内加上时间定时检查。线程安全问题往往并不容易被发现,而被发现时又往往难以复现,这对我们一次设计完善代码的能力提出了更高的要求。

2.从层次化设计方面:

主要是设计模式上的选择。采用不同的设计模式:如工厂模式、流水线模式、需求链模式等,就需要建立相应的层次化模型。层次化设计能够将复杂系统问题分解为多个层次或模块。在软件开发、系统设计和问题解决的过程中,层次化设计通过分解大问题为小问题,然后逐级解决,使得设计过程更加简单和可控。每个层次或模块都负责特定的功能或任务,上层模块关注高层次的功能和实现,而低层次模块负责实现具体的细节。这种设计方式有助于提高系统的可维护性和可扩展性,因为每个模块都可以独立地进行修改和测试。

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值