第二单元总结博客

第二单元总结博客

同步块和锁

》》》本次电梯以第三次课上实验代码为基础,将ProcessingQueues以及WaitQueue作为共享对象。
》》》作业中有加锁的函数,如下
public synchronized void addRequest(MyReq request) { requests.add(request); }
》》》如果不加锁,当两个线程同时调用该函数时可能会报出一个数组越界的异常,造成线程安全问题。

死锁问题

》》》事实上,课上实验的代码时存在出现死锁的可能性的,原因就在于调度器线程Scheduler和电梯线程Process中使用不同的顺序嵌套地对waitQueue和processingQueue进行加锁,其中Process如下中的相关代码块如下
synchronized (processingQueue) {
synchronized (waitQueue) {
if (processingQueue.isEmpty() && waitQueue.noWaiting() && waitQueue.isEnd()) {
return;
}
}
try {
processingQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

》》》为了解决该问题,可将代码块修改如下,将waitQueue的锁从processingQueue的锁中取出,以避免嵌套。

synchronized (waitQueue) {
if (processingQueue.isEmpty() && waitQueue.noWaiting() && waitQueue.isEnd()) {
return;
}
}
if (processingQueue.isEmpty()) {
synchronized (processingQueue) {
try {
processingQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

调度器的设置

》》》第一次作业为单电梯,调度器的功能无法得到体现,本节主要介绍第二、三次作业的调度器。

uml类图及协作图

image
image

第二次作业

》》》本次作业为规格相同的多架电梯,调度器的核心思想为让不同电梯接收终点在指定范围的请求,通过公式
(X * 电梯总数 - 1)/20
来判断乘客请求将要进入的电梯,使用这种方法的优点就是能够将不同的请求分配到不通过的电梯中,并且能够根据电梯数量进行动态调整。例如,当前有4架电梯(偏好0,1,2,3),那么终点楼层为1到5的乘客就会被分配到0号电梯。至于为什么选择终点楼层而不是起始楼层,是基于以下考虑:Night模式下会涌入大量起始楼层为1的乘客请求,导致0号电梯一人抗下所有,而这并不是我希望看见的。
》》》当然,仅仅依靠上述调度在面对极端情况时性能依然很差,于是我应用了梯度。具体表现为,如果同时涌入大量终点楼层为1的乘客请求,如果此时梯度为d,经过调度器的调度以后编号0的电梯队列中请求数为x,则编号y的电梯请求数为x - y * d。关于d的大小,我将其设置为6 - x,道理也是为了让它能够随电梯的数量而改变。
》》》第二次作业的调度器在强测中表现出良好的性能,我认为这是一个成功的调度器。

第三次作业

》》》本次作业有多种电梯,因此我的调度器的主要功能就是把乘客请求分配到正确的电梯中去,必要时对乘客请求进行拆分。
》》》首先,能够进入C类电梯的请求就分配到C类电梯,其次,能进入B类电梯就分配到B类电梯。最后将起始层为单数并且起点和终点的差值大于3的请求拆分,过程类似1-8拆分为1-7和7-8,将1-7加入B,7-8加入A。其余全部加入A。
》》》如果新增电梯,则对新增类型的电梯加入第二次作业的分配判定。
》》》优化方向:如果有大量1-20层的请求,那么程序性能就会非常差劲,优化方向为,按照梯度将部分请求进行拆分,并分别加入到A、B类型中。

架构设计和可扩展性

image
》》》输入的请求会进入等待队列,调度器将等待队列中的请求分配到各个电梯队列。

可扩展性

》》》由于采用了课上实验的架构,在对其死锁问题进行解决以后,程序的可扩展性极高,在后续的作业中,几乎只用根据调度策略修改调度器代码即可完成需求,当然,还需要在请求类中添加相应的属性。
》》》代码可扩展性高的另外一个原因是我通过实现了一个优秀的可捎带的Random方法来处理全部的三种模式,这使得我在进行扩展时只需要稍微修改一下Random方法。

bug

》》》死锁。即上文提到的死锁。
》》》梯度过大问题。第二次作业,原本设置的梯度等于电梯的数量,最终的结果就是由于梯度太大导致被极端数据hack,将梯度改为与电梯数量负相关后bug得到修复。
》》》盲目拆分。第三次作业在拆分请求时,没有对电梯的起点终点间隔加以判断,出现了7-8层被拆分为7-7和7-8的无限循环过程。

测试策略

》》》由于还没有发明自动生成样例的工具,以及定时输入的工具,在进行hack时只能手动构造极端样例,但是我认为在本次电梯作业中构造极端样例并没有什么现实意义,徒增被hack同学烦恼,因此在互测屋中活跃度极低。

心得体会

》》》一个良好的架构设计能够让程序设计事半功倍,但是本次作业的架构设计雏形并不是自己亲自建立的,因此需要多次回顾以加深印象。同时,在多线程程序的设计过程中,要时刻注意线程安全问题。最后,在对自己程序进行改进的过程中,一定要足够耐心,加深对相关知识的了解,有目标的进行修改,而不是想当然的随机修改,期待奇迹的发生。

posted @ 2021-04-26 22:40  weileng  阅读(63)  评论(0编辑  收藏  举报