代码改变世界

oo第二单元总结

2019-04-23 16:51  mxj1  阅读(160)  评论(0)    收藏  举报

从从多线程的协同和同步控制方面,分析和总结自己三次作业的设计策略。

及基于度量来分析自己的程序结构

第五次作业

采用了生产者-消费者模型,输入线程作为生产者线程,电梯线程作为消费者线程,建立了一个PersonQueue类来充当Tray。通过分析,PersonQueue不仅要有一个储存已经输入但还未被处理的请求的队列,还应有一个变量来传递输入线程结束这个信息。所以我在PersonQueue里有一个PersonRequest的Arraylist,以及一个布尔变量end。输入线程负责获取请求,将请求放进队列,并notify()告知电梯线程有请求进入。电梯线程循环查询请求队列以及结束标志,获得请求并按照FIFO处理请求。在查询请求队列的过程中,如果请求队列为空则wait(),等待输入线程获得请求并将其唤醒。在输入了结束信号时,输入线程将PersonQueue中end设为True,即PersonQueue不会再接受到新的请求,同时结束输入线程。在电梯线程中,如果查询到结束标志end已经为true以及请求队列为空,则表示电梯已经完成自己的工作,可以结束。

值得注意的一点是我在PersonQueue类里设置了两个方法isEmpty和getEnd来分别查询请求队列是否为空和结束标志是否为true,所以为了保证进程同步的正确性只能在电梯线程中先对PersonQueue上锁再进行这两步操作。

while (true) {
    synchronized (queue) {
        if (queue.getEnd() && queue.isEmpty()) {
            break;
        }
    }



可以看到类与方法都很简单,因为要实现的算法不是很难。

第六次作业

第六次作业中电梯的运行策略由FIFO的傻瓜电梯转向了ALS可捎带电梯,这就要求电梯线程能够按照楼层和方向查询请求队列中是否含有可捎带的请求。为了能够使电梯记录已经上电梯的乘客,在电梯线程中增加了一个存放已经上电梯乘客的容器。电梯线程循环执行查询请求队列以及结束标志,获得主请求并在运送主请求的过程中进行捎带,当电梯乘客列表为空时表示完成了一次捎带,进入下一次循环。其中查询请求队列wait()的标志以及电梯线程结束控制与第五次作业相同。

elevator的move方法写的较为冗长,主要是在move中又实现了捎带乘客和将乘客放下电梯,在第七次作业中改用pickUp和putDown方法实现这两个功能。

第七次作业

第七次作业要求实现多电梯,为了能够实现请求到电梯的分发,我认为有两种方法。一是实现一个调度器,调度器将请求分配给各个电梯。二是每当有请求进入请求队列后,由各部电梯去查询自己能否执行请求,由电梯去争抢请求。我实现的是第一种策略,即在输入线程与电梯线程之间加一层调度器。电梯在保存一个被调度器分配的请求的容器。这样,输入线程通过调用调度器,调度器选择一个电梯将请求加到电梯请求容器来完成线程间的信息交换。如果多部电梯都能执行对应请求,则选择一个等待乘客最少的电梯分配任务。电梯沿用第六次作业的ALS捎带算法。

为了应对需要两部电梯合作的情况,为了避免复杂的线程同步,我继承PersonRequest类写了Request类,另增加了一个对第二个请求的引用以及一个标志位。对于需要拆分的请求,第一个请求保存对第二个请求的引用,同时将第二个请求的标志位设为0,表示当前请求不能被执行。当第一个请求到达指定楼层后,将第二个请求的标志位设为1,表示第二个请求可以被执行。

对于如何结束电梯线程,我在电梯类中设置了end变量,当输入线程读到null时,输入线程调用调度器通知三个电梯线程输入已经结束。电梯线程在end为true且将自身请求全部运算完毕后即可结束线程。

scheduler中的split较为复杂,主要是因为使用了大量if-else语句,对需要拆分的请求进行硬编码拆分。选择硬编码是想减少调度器与电梯线程间的信息交流,避免电梯线程被调度器阻塞。

分析自己程序的bug

这单元我在第六次第七次作业中强测都出现了bug,自我反省一下还是自己测试较少的原因。因为第六次第七次作业都是在周一晚上完成的,留给自己的测试时间较少,加之在第一单元中测较强,中测没有bug强测一般也没有bug的,这就给自己留下了侥幸心理。总而言之还是自己的懒惰导致了强测出现bug。

第五次作业

因为算法上没有特别复杂,主要测试了线程同步间是否出现问题,特别是线程结束的部分。

第六次作业

bug出现的问题在于电梯内部的ALS算法实现,无关于线程同步。出现的问题在于,当去主请求所在楼层时如果捎带,电梯在执行完捎带请求后没有继续运行到主请求的from楼层,导致主请求进入电梯的楼层出现问题。

第七次作业

bug出现的问题在于调度分配算法,物管员线程同步。出现的问题在于,少考虑了一种需要拆分请求的情况,导致特殊情况下无法将请求送达。

分析他人程序的bug

我认为本单元作业出现的bug主要分两类:业务逻辑错误以及线程安全问题。业务逻辑错误主要出现在第六次以及第七次作业中,主要可能的情况就是捎带处理不正确或者第七次作业中的电梯运行楼层不正确。
线程安全问题主要是是否有线程未被正常关闭导致run time error。

心得体会

线程安全

1.对于被可能被多线程同时访问的变量需要上锁,保证同一时刻只有一个线程访问该变量

2.wait()和notify()要对同一对象上锁才能实现。

设计原则

第一次作业是简单的生产者消费者模型,消费者电梯只需要一个一个去托盘(请求队列里)获得请求即可。第二次作业需要消费者电梯能够查询托盘中的内容,有目的的从托盘中获得请求。第三次作业需要将请求按照一定规则分配给消费者。总结来说,如果第一次作业能够实现好的生产者消费者模型,理清生产者-托盘-消费者的关系,第二次第三次作业即可在原有设计上扩展电梯及托盘功能实现。