1.程序架构
1.1 作业一:单部ALS电梯
第一周的作业虽说是要求多线程,但是也只是生产者多线程,也就是多个InputThread线程不断向调度器中投入Passenger,(但电梯只有一个)在执行的过程中,需要给共享对象,也就是调度器加锁,防止线程不安全出错。我使用的是简单的synchronized + notifyAll() 的方式,由于InputThread只需要放Passenger,放完就结束进程;而电梯取Passenger更加复杂,需要判断上下行,是否开门,楼层问题,因此我将调度交给了电梯的run方法。
我使用的算法是SSTF,具体而言,run方法结构如下:
while (true) {
while (mainrequest != location) {
if (mainrequest > location) { // up
......
}
} else { // down
......
}
//a turn ends
}
while (mainrequest == location) {
Passenger finded = null;
synchronized (controller) {
while (finded == null) {
if (controller.getWaitqueue().size() == 0) {
if (inputover && passrequest.size() == 0) {
end = true;
break;
}
try {
controller.wait();
} catch (InterruptedException e) {
;
}
}
finded = controller.findNew(location);
}
controller.notifyAll();
}
if (end) {
break;
}
mainrequest = finded.getFrom(); // fetch
if (mainrequest > location) { // up to fetch
......
}
else { //down to fetch
......
}
this.setOpenning(); //open
this.getIn(finded);
try {
Thread.sleep(400);//wait(400);
} catch (InterruptedException e) { ; }
this.setOpenning(); //close
}
if (end) {
break;
}
基本的思想是,当主请求和当前位置不等时,移动到主请求位置,并在每一层将合适的人拉近电梯,若新来者主请求更近,则更换主请求至稍带请求;当主请求和当前请求相等时,放出要下的乘客,更换主请求,若此时满足结束条件,则结束电梯线程。
类图如下:
1.2 第二周:多部电梯
这周的作业要求能实现多部电梯,并且每部电梯有了编号,载客人数的限制。总体来多,迭代开发难度不大,下面简述以下主要改变内容。
首先Elevator需要增加capasity成员以及isFull()方法,只有未满时才可以加入Passenger;其次,在Main里面构造一个电梯线程类的数组,循环产生制定数目和ID的电梯线程。此外,还需要增加楼层,以及特殊处理负层的输入输出(没有0楼),下面附上类图:
1.3 第三周:多类型电梯
这周的一个拓展重点是增加了电梯的类型,不同类型电梯具有不同capasity,同时可停靠楼层也各有差异,根据A、B、C三类共有的1、15层,我们可以规定,让韦恩图中跨圈的起始站在邻近的共有层中转,即一开始就确定中专方案,并且在Passenger中增加needTrans成员,以此在下电梯时判断是否需要再次进入电梯,如果需要,就利用InpurThread再次投入waitqueue,并将needTrans设为false。下图中,黄色位A型,红色为B型,蓝色为C型。
代码实现上我设置了六个数组:
private static final int[] stopA = new int[] {-2, -1, 0, 1, 15, 16, 17, 18, 19, 20 };
private static final int[] stopB = new int[] {-1, 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
private static final int[] stopC = new int[] {1, 3, 5, 7, 9, 11, 13, 15 };
private static final int[] uniqA = new int[] { -2, 16, 17, 18, 19, 20 };
private static final int[] uniqB = new int[] { 2, 4, 6, 8, 10, 12, 14 };
private static final int[] uniqC = new int[] { 3 };
在到达楼层时判断是否能加入乘客,并判断是否需要换乘。
此外,还需要在Main中处理增加电梯请求。
2 BUG & DEBUG
作业序号 | bug类型 | 原因 |
---|---|---|
2 | RUNTIME ERROR | ArrayList允许添加null值,造成了list内的对象转换出现异常 |
2 | WA | 最高楼层未设置为19(16+3) |
3 | 超时 | 有电梯上了不该上的人,以致无法释放 |
3 | RUNTIME ERROR | 当有不会用到的电梯时,轮询过频繁,cpu时间过长 |
3 心得体会
这次作业的主题是多线程调度,我的感受是:多线程与单线程编程有很大的不同,我们往往更加考虑线程安全和线程之间的交互,反而对效率和时间复杂度不那么重视。而且我们面对的问题往往牵扯到多个对象同时处理一类对象,进行多对多的操作,因此我们的中心应该是将程序各个线程之间工作量是否平衡,利用率是否合适。由于采用了锁,我们要规范使用try-wait-catch的形式,并且在合适的地方notifyAll()。最后,在处理线程结束时需要设立一些标志位,可以时全局的,告诉我们整体工作是否完成;也可以是局部的,告诉我们这一线程工作是否完成。需要着重注意的一点是,我们需要预防死锁的形成,这就意味着要斟酌每一处加锁是否必要,因为不合理的加锁会大大影响多线程并发效率,使程序退化。
Last but not least,欢迎访问个人网站原文地址:http://www.brandonmoo.xyz/2020/04/18/oo-unit-2/