多线程电梯调度——OO Unit 2

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/

猜你喜欢

转载自www.cnblogs.com/brandonmoo/p/12725737.html