• 第二单元博客作业


    第二单元博客作业

    PART 1 设计策略

    ​ 本周的电梯作业要求采用多线程的实现方式,三次作业之间进行采用相同的设计策略。

    ​ 多线程的协同方面,本次作业均采用相同的设计构造,主要运用了生产者-消费者模式。程序大致分为以下几类:

    ​ ·输入类 由于输入的内容较少,并没有新开input线程,而是在main线程中实现输入

    ​ ·请求队列类(Queue) 应用单例模式,用于存放请求。相当于生产者-消费者模型中的缓冲区,由输入类存放请求,电梯类通过调度器类来获取请求(后续并入了Controller中)

    ​ ·控制器类(Controller) 应用单例模式,用于电梯的调度。控制器类会获取请求队列的信息并存放在外部请求map和电梯内部请求map中,并且实现乘客进出电梯,控制电梯的移动等

    ​ ·电梯类(Elevator) 电梯类,实现移动,开关门,其余工作通过调用controller实现

    ​ 在同步控制方面,本次在线程安全上均采用synchronized关键字而不是lock(可能是因为懒得对代码重构了)。值得注意的是,通过wait()方法只会释放当前对象的锁而不是该线程获的得所有的锁,就比如一个电梯通过Controller访问Queue,然后Queue中进行的wait()只会释放Queue的锁,controller的锁扔在这个电梯线程的手中,其他电梯无法访问Controller。就因为这个原因,如果多层访问synchronized方法程序很可能会出现死锁,为了避免这种情况出现,后续的设计中删除了Queue,在Controller中实现对应的功能。

    PART 2 第三次作业架构的可扩展性

    ​ Elevator类:

    ​ 第三次作业增加了不同种类的电梯,并且希望不同种类的电梯能进行合理的调度。在我的功能设计中,电梯类保存所有本电梯的信息,包括名称、类型、电梯编号等,然后电梯的run方法如下:

            @Override
    public void run() {
        try {
            Thread.sleep(600);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (!noMore || !controller.allDone()) {
            controller.check(id, type);
            if (controller.hasPa(curFloor, id, capacity, type)) {
                try {
                    openDoor();
                    closeDoor();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            isUp = controller.setIsUp(curFloor, id, type);
            if (!controller.done(id, type)) {
                try {
                    move();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    ​ 可以看出,电梯内部只实现了开关门、移动一层的方法,乘客的移动、决定运行方向、电梯是否运行等都是通过controller来实现的。

    ​ 后续对于电梯的各种要求(比如电梯类型的增加),可以直接通过电梯类中的属性和方法来实现,具有较好的可扩展性

    ​ Controller类

    ​ Controller类主要有两项功能:接受输入类的请求输入,然后根据内部的动态信息,在电梯调用相应的方法时做出反应。电梯内部的保存属性如下:

    ​ noMore属性用来判断是否输入已经结束

    ​ outMap用来存放在电梯外的队列。共有六个队列,分别表示A电梯响应的,B电梯响应的,C电梯响应的,A、B电梯都可响应的,B、C电梯都可响应的,A、B、C电梯都可响应的。然后每个队列是一个HashMap,其Key值是请求的fromfloor,这样可以做到快速的查找

    ​ inMap用来存放电梯内的队列,分别表示不同的电梯内部的乘客请求队列。每个请求队列是一个HashMap,其Key值是请求的tofloor

    ​ typeOf存放的是不同电梯能去到的楼层,后续用来判断请求交给哪个电梯处理

    ​ 根据以上的属性来分析,后续如果出现动态创建电梯(电梯可达楼层自定)也可以通过改变typeOf的创建来实现,如果出现电梯数量的增加,请求队列的增加,也可以通过几个map的函数来实现。其它的请求也能在controller中新增方法完成

    PART 3 基于度量的程序结构分析

    ​ 第一次作业

    ​ 第一次代码的设计架构较为良好,没有出现太大的问题,metrics分析代码的结构化程度较高,耦合度低,复杂度较低,最终的测试也是拿到了较好的成绩。

    ​ 第二次作业

    ​ 第二次作业中由于增加了电梯的数量,在controller中需要增加的方法较少,只是对于一些方法进行了修改,但是这样也导致了部分方法出现了代码复杂度提高,结构化程度较低。

    ​ 第三次作业

    ​ 第三次作业需要增加的功能较多,代码修改量较大,将这些方法全部挤在controller中是一种写起来比较方便的方法,但是这样大大降低了代码的结构化程度,增加了复杂度和可能产生bug的风险。从复杂度分析可以看出,controller中已经有多个方法出现了过于复杂的现象。其实更好的办法是采用两级调度,通过总调度器将请求分散后通过各自独立的调度器进行调度,可能能得到更好的代码结构。

    PART 4 分析自己程序的BUG

    ​ 第一次作业

    ​ 第一次作业出现的bug较少,只被刀了一次,那个也不是严格意义上的bug,只是在特定情况下调度策略的问题,导致超时。在优化了算法之后顺利解决。

    ​ 第二次作业

    ​ 第二次作业出现了本单元中最严重的一个bug,也是多线程安全问题中非常重要的一环——死锁。具体的内容在上面的同步控制内容中已经提到了,就是多个synchronized方法嵌套调用时,由于wait方法不会释放所有的锁从而导致了死锁的产生。为了de这个bug我前前后后修改了快十个版本,最终的额解决方案是删除单独出来的Queue类,并将其功能并入Controller类中。在今后的设计中,应当避免不同类之间的synchronized方法嵌套,以免出现死锁的问题

    ​ 第三次作业

    ​ 第三次作业出现了两个bug:新建电梯时数组溢出,2/4-TO-3和3-TO-2/4请求出现奇怪的错误。这些都是初期测试不充分导致的,今后应当极力避免这种低级错误

    PART 5 发现别人程序BUG采用的策略

    ​ 因为本单元没有构建测评机的关系,导致我在互测中活跃程度较低,没有发现多少别人程序的BUG

    PART 6 心得体会

    ​ 本周作业重点在于多线程的实现,算是解除了全新的编程领域,之前学过的C还没有关于这一部分的内容。个人感觉本周作业完成的情况较为良好,吸取了第一单元的教训后,本次的作业没有出现像第一单元那样互测都进不了的惨状。在写第二次作业时,曾运用了一种取巧的结构一小时完成,但是后来想到扩展性方面的内容便果断重构了(还好重构了,不然第三次要从头开始)。

    ​ 我个人对本周的完成情况较为满意,唯一不足的是第三次作业因为测试没有充分的原因强测一开始很多点没过,如果一开始测试充分的话强测拿到95分以上是肯定没有问题的,这样一想便显得极为可惜。今后还需要更加重视测试方面的内容(不要偷懒,不搭自动测评机还不手测)。希望之后能够吸取教训,更进一步。

  • 相关阅读:
    python学习-流程控制(四)
    如何将子分支代码推送到新建的仓库
    python学习-列表、元组和字典(三)
    jmeter-操作mysql
    jmeter-控制业务比例
    python-使用session记录登录状态
    python-json与字典的区别
    cookie session token 详解
    python-装饰器
    unittest断言
  • 原文地址:https://www.cnblogs.com/yokies/p/12708100.html
Copyright © 2020-2023  润新知