BUAA_OO_2022 第二单元总结

OO 第二单元 电梯 总结 

目录

  • 一、三次作业分析

  • 二、bug分析

  • 三、心得体会

在第一部分将结合三次作业的uml类图,介绍每次作业的设计思路关键点、同步块与锁的选择以及调度器迭代,再展示最后的UML协作图。

在第二部分将介绍程序bug、hack和test策略。

在第三部分讲述心得体会。

一、三次作业分析

1. 第五次作业

UML类图

 

 

 

设计思路

第五次作业要求实现5个单座电梯的运行模拟。

由于第五次作业仅仅为每座1个电梯,主要考察点在于线程的使用和线程安全问题。考虑到后续的作业拓展,第五次作业我采用了输入线程+分发线程+电梯线程的三线程设计。以RequestQueue类对象作为共享对象,实现输入线程与分发线程,分发线程和电梯线程之间的交互。对于电梯的运行,我采用look策略,在run函数中维护电梯的direction变量,并且采用状态机的方法进入move函数

同步块与锁的设计

第五次作业在我的设计中,有下列情况需要加锁:

  • 共享对象方法加锁:在共享对象类内定义的方法中,如果涉及对其的读写操作,都需要加锁。

  • 线程中加锁同步块:一些方法不便于直接定义在共享对象类中,但涉及对共享对象的读写操作,这时需要将其加锁。

synchronized (processingQueue) {
try {
//System.out.println("Prccess " + type + " wait new task");
direction = Direction.stop;
processingQueue.wait();
//苏醒以后再次判断该怎么运行
//System.out.println("Prccess " + type + " wait end becauseof new task");
continue;
} catch (InterruptedException e) {
e.printStackTrace();
}
}

 

调度器设计

第五次作业我设计了简单的调度器线程,我采用的方法是将输入线程的请求存放在等待队列中,再从中不断将请求分配到电梯的等待队列。电梯作为线程运行,使用状态机不断更新输出,策略是look。

2. 第六次作业

UML类图

 

 

 

 

设计思路

第六次作业增加了动态增加电梯和环形运输的需求。

对于动态增加电梯需求,我直接在InputThread中进行线程创建并且分配出对应的等待队列并运行,用来响应增加电梯请求,而分派乘客的流程与第五次作业一致。

对于环形运输需求,可以考虑把A->B->C->D->E->A定义为shun(顺)方向,反向定义为ni(逆)方法。我更新了方法类Direction(之前已经有了up,down,stop),经过我的分析,环形运输电梯实际上和垂直方向的电梯基本没有逻辑上的区别,只需要针对环路更改对应的move方法,沿用状态机,以及更新run中环形运输的look策略即可。

本次设计包括实现非常顺利,从中看出第五次作业的架构复用性极佳,给第六次作业打好了基础,同时逻辑架构清晰,能够很快将新的需求转化为具体的代码实现。

同步块与锁的设计

由于增加了新的电梯,同一张候乘队列可能有多个电梯访问,因此,在第六次作业中的电梯的候乘表在执行非上锁行为时需要加锁,其余的锁保持。

synchronized (processingQueue) {
Iterator<ArrayList<PersonRequest1>> iterator = processingQueue.getRequests().iterator();
while (iterator.hasNext()) {
ArrayList<PersonRequest1> personRequest1 = iterator.next();
if (capacity == maxcapacity) {
return;
} else if (personRequest1.get(0).getFrom() == floor
&& personRequest1.get(0).getDirection() == direction) {
if (capacity == 0) {
direction = personRequest1.get(0).getDirection();
}
servedrequest.add(personRequest1);
iterator.remove();
Out.out(
String.format("IN-" + personRequest1.get(0).getId() + "-" +
building + "-" + floor + "-" + id));
capacity += 1;
}

}
processingQueue.notifyAll();
}

 

调度器设计

本次作业中,增加了一个调度器线程实例用于给环形方向的电梯分配任务,并管理对应的10层楼的候乘表。

3. 第七次作业

UML类图

 

 

设计思路

第七次增加了跨楼座和层之间的请求,并且电梯可一定程度自定义化,增加了可修改的属性(速度,容量等),以及停靠限制。

对于电梯可修改属性和停靠限制,处理很简单,只需要给电梯增添对应的属性并修改构造方法即可。

本次设计的关键点如下:

对于生成横向电梯的请求,将新的可达路径加入path。

对于跨楼座和层之间的请求,采用在输入线程静态分解的方式,通过Path类分析,得到分解后的最多三个子请求组成的请求列,具体分解方式参考停靠限制、纵向距离最短原则。

其次在调度器类中增加了计数器,在分配出由input线程加入的子请求数时增长,而在电梯在处理完一个子请求时将剩余未完成请求重新添加入对应的横向或纵向队列,并且修改已完成请求对应的调度器实例的计数器,同时修改调度器类的结束条件,即多一个计数器需要处于清零的状态。

同步块与锁的设计

在第七次作业中,没有太多与第六次作业在线程安全的问题的区别,不再赘述。

调度器设计

第六次设计上增加了计数属性极其对应的方法。

可扩展性分析

架构具有良好的可扩展性,从第五次作业开始,该三类线程分工明确,基本框架复用率很高。

4. UML协作图

以第七次的作业的协作图为例,第五次和第六次的协作图包含在其中

二、bug、test及hack

bug

第五次作业输出时线程安全出现了问题,增加了一个线程安全类后解决。

第六次作业的主要出现在了线程安全上,锁的对象出现了问题,且在调用getRequest方法时方法没有上锁。

第七次作业出现了电梯停靠非可达楼座的问题,导致了WA,同时捎带的时候也出现了这个问题,说明自己的测试不够充分,只不过公测的测试数据确实好弱。

test

由于多线程的不可复现性,因此,我在第五次测试方法主要是随机数据进行大量测试,但是发现效果不理想,本地的竞争程度没有评测机激烈,我认为需要构造特殊的数据才能有效“复现”线程安全问题。为此,针对第六次和第七次作业我不再使用数据生成器,而是构造短时大量的同质数据输入来测试。

对比第一单元,第二单元大多bug在线程安全上,而不是局限于覆盖性的问题,因此测试策略有所不同。

hack

我的hack策略为大量随机数据攻击。第五次作业时使用随机生成器进行了hack。第六次和第七次作业只是随机进行了少量hack。

三、心得体会

线程安全

电梯让我初次接触了多线程编程。对线程安全以及锁的理解非常重要,为此查阅了不少资料,同时也翻看了不少博客。三次作业中强测中一次因为线程安全问题丢分,两次hack丢分,经过两次作业对线程安全理解有所加深,但是在最后一次作业中出现了较大失误,电梯停靠和捎带出现了问题,结果草草收场。

层次化设计

本次层次化设计在线程协作部分体现得很好,三个线程类分工明确,构成流水线设计,但是在电梯线程本身设计上其实还可以更近一步,还可以将run函数的运行逻辑进行进一步拆分,把策略和具体物理运行分得更加细致。

感想

电梯单元结束,对于这道北航oo的经典题目,我认为它是多线程编程入门的很好的一道题目。题目贴切生活,较为形象,同时又充分涉及了线程安全的相关入门知识,并且对层次化设计提出了新的挑战。虽然bug复现比较困难,但是还是让我对多线程编程有了初步的了解,收获很大。

posted on 2022-04-29 13:46  Lzchhh  阅读(18)  评论(0编辑  收藏  举报

导航