第二单元总结

第二单元总结

作业总结

一 程序设计策略

这三次作业我采取的设计策略都差不多: 电梯 输入 调度 线程架构

  1. 输入线程负责获取并解析输入的请求, 并将解析之后的结果交给调度器
  2. 调度线程负责将请求分发给每个电梯
  3. 电梯线程负责将调度器交给自己的请求完成

具体而言, 第一次输入不需要解析, 输入线程仅仅是用来将输入请求搬到调度器中. 调度线程也只是简单地将请求全部都给电梯. 电梯线程每次检查是否有任务, 若有则完成之.

第二次作业, 增添了捎带, 对于输入线程和调度线程而言, 仍然没有改变, 但是电梯有了变化, 增加了一个主请求机制, 需要确保任何时候, 只要电梯在运行就一定是在为主请求服务, 其他的都是捎带的, 并且是否捎带取决于主请求的性质. 因此需要保证任何时候都拥有主请求的最新信息. 简而言之, 每当主请求可能发生变动时都需要检查主请求并将更新之(包括被notify时, 有人出去时, 有人进来时).

第三次作业, 由于换乘的加入, 需要将一个请求拆分成两次, 使得调度更为复杂. 我在输入处理时将PersonRequest重新封装成一个Request类, Request实例构造时自动静态地拆分请求为两部分(如果一步可达则第一部分无效), 实例会先向外部暴露第一步, 当第一步完成(失效)后, 暴露第二步, 当第二步完成后暴露一个完成的标志给外部(0->0). 这样, 调度器按照拆分的顺序分配给各个电梯, 各个电梯完成之后再将请求返还给调度器, 调度器判断这个请求是否需要再次调度, 若需要则分配给电梯, 否则丢弃.

通过拆分封装机制, 避免了两步同时执行或者顺序颠倒的问题. 但由于时静态拆分, 所以不能根据电梯的载客情况动态地调整.

二 程序结构分析

  • 类图

    我三次作业都采用了一个策略, 所以类图基本是一样的,这里仅贴出第三次作业的类图:

  • 度量

    第一次:

    Method metrics:

    method ev(G) iv(G) v(G)
    com.mypkg.ElevatorThread.ElevatorThread(ArrayList,Status) 1.0 1.0 1.0
    com.mypkg.ElevatorThread.move(int) 1.0 2.0 3.0
    com.mypkg.ElevatorThread.run() 3.0 4.0 5.0
    com.mypkg.ElevatorThread.work(PersonRequest) 1.0 7.0 7.0
    com.mypkg.InputThread.InputThread(ArrayList,Status) 1.0 1.0 1.0
    com.mypkg.InputThread.run() 3.0 3.0 4.0
    com.mypkg.Main.main(String[]) 1.0 1.0 1.0
    com.mypkg.Status.get() 1.0 1.0 1.0
    com.mypkg.Status.set(int) 1.0 1.0 1.0
    com.mypkg.Status.Status(int) 1.0 1.0 1.0
    Total 14.0 22.0 25.0
    Average 1.4 2.2 2.5

    Class metrics:

    class OCavg WMC
    com.mypkg.ElevatorThread 2.0 8.0
    com.mypkg.InputThread 2.0 4.0
    com.mypkg.Main 1.0 1.0
    com.mypkg.Status 1.0 3.0
    Total 16.0
    Average 1.6 4.0

    第二次:

    Method metrics:

    method ev(G) iv(G) v(G)
    com.mypkg.Dispatch.Dispatch(Elevator,Input) 1.0 1.0 1.0
    com.mypkg.Dispatch.run() 3.0 6.0 6.0
    com.mypkg.Elevator.check() 1.0 18.0 20.0
    com.mypkg.Elevator.Elevator() 1.0 1.0 1.0
    com.mypkg.Elevator.insert(PersonRequest) 1.0 1.0 1.0
    com.mypkg.Elevator.run() 4.0 6.0 6.0
    com.mypkg.Elevator.setStatus(boolean) 1.0 1.0 1.0
    com.mypkg.Elevator.work() 3.0 8.0 11.0
    com.mypkg.Input.get(int) 1.0 1.0 1.0
    com.mypkg.Input.getQueue() 1.0 1.0 1.0
    com.mypkg.Input.getStatus() 1.0 1.0 1.0
    com.mypkg.Input.Input() 1.0 1.0 1.0
    com.mypkg.Input.remove(int) 1.0 1.0 1.0
    com.mypkg.Input.run() 3.0 4.0 4.0
    com.mypkg.Input.size() 1.0 1.0 1.0
    com.mypkg.Main.main(String[]) 1.0 1.0 1.0
    Total 25.0 53.0 58.0
    Average 1.5625 3.3125 3.625

    Class metrics:

    class OCavg WMC
    com.mypkg.Dispatch 3.0 6.0
    com.mypkg.Elevator 5.0 30.0
    com.mypkg.Input 1.2857142857142858 9.0
    com.mypkg.Main 1.0 1.0
    Total 46.0
    Average 2.875 11.5

    第三次:

    Method metrics:

    method ev(G) iv(G) v(G)
    com.mypkg.Dispatch.checkIn(int[],int) 3.0 1.0 3.0
    com.mypkg.Dispatch.Dispatch(Elevator,Elevator,Elevator,Vector,Input,Status) 1.0 1.0 1.0
    com.mypkg.Dispatch.insert(Person) 2.0 11.0 12.0
    com.mypkg.Dispatch.run() 4.0 11.0 11.0
    com.mypkg.Elevator.check() 3.0 13.0 16.0
    com.mypkg.Elevator.closeDoor() 1.0 3.0 3.0
    com.mypkg.Elevator.Elevator(String,Vector,Status) 1.0 7.0 7.0
    com.mypkg.Elevator.getStatus() 1.0 1.0 1.0
    com.mypkg.Elevator.insert(Person) 1.0 1.0 1.0
    com.mypkg.Elevator.openDoor() 1.0 3.0 3.0
    com.mypkg.Elevator.run() 4.0 7.0 7.0
    com.mypkg.Elevator.setStatus(boolean) 1.0 1.0 1.0
    com.mypkg.Elevator.work() 3.0 9.0 12.0
    com.mypkg.Input.get(int) 1.0 1.0 1.0
    com.mypkg.Input.getQueue() 1.0 1.0 1.0
    com.mypkg.Input.getStatus() 1.0 1.0 1.0
    com.mypkg.Input.Input(Vector) 1.0 1.0 1.0
    com.mypkg.Input.remove(int) 1.0 1.0 1.0
    com.mypkg.Input.run() 3.0 4.0 4.0
    com.mypkg.Input.size() 1.0 1.0 1.0
    com.mypkg.Main.main(String[]) 1.0 1.0 1.0
    com.mypkg.Person.checkIn(int[],int) 3.0 1.0 3.0
    com.mypkg.Person.find(int[],int[],int,int) 1.0 4.0 7.0
    com.mypkg.Person.finish() 2.0 1.0 3.0
    com.mypkg.Person.getFromFloor() 2.0 1.0 2.0
    com.mypkg.Person.getId() 1.0 1.0 1.0
    com.mypkg.Person.getToFloor() 2.0 1.0 2.0
    com.mypkg.Person.Person(PersonRequest) 1.0 17.0 17.0
    com.mypkg.Person.toString() 1.0 1.0 1.0
    com.mypkg.PersonTest.main(String[]) 1.0 2.0 2.0
    com.mypkg.Status.getStatus() 1.0 1.0 1.0
    com.mypkg.Status.setStatus(boolean) 1.0 1.0 1.0
    com.mypkg.Status.Status() 1.0 1.0 1.0
    Total 53.0 112.0 130.0

    Class metrics:

    class OCavg WMC
    com.mypkg.Dispatch 4.5 18.0
    com.mypkg.Elevator 4.666666666666667 42.0
    com.mypkg.Input 1.2857142857142858 9.0
    com.mypkg.Main 1.0 1.0
    com.mypkg.Person 2.875 23.0
    com.mypkg.PersonTest 2.0 2.0
    com.mypkg.Status 1.0 3.0
    Total 98.0
  • 问题分析

    从三次代码的度量中容易看到, 都有一两个方法复杂度很高. 基本都是因为if-else了...开门之前判断 关门之前判断 等等...如果将这些分别写成一个个小函数, 应该是可以显著避免这种情况的. 第三次中大量的复杂度过高主要是因为我拆分请求的时候智障地使用了大量的if-else(case: AB BA AC CA BC CB), 插入请求的时候也是if-else(case A B C NULL). 所以复杂度比较高.

    这暴露出了我思考的惰性, 以及编程时思维的不严谨, 看官应该引以为戒.

    经过事后思考, 以上问题都应该是可以避免的, 以后的作业中我将尽量避免这种ugly的写法.

三 程序bug分析

逻辑上没什么问题, 基本就是细节bug.

一个值得关注的地方是结束线程, 输入线程结束之后, 调度线程和电梯线程如何结束, 如果电梯先结束, 可能有请求还没处理, 如果调度线程先结束, 可能电梯中得请求还需要二次调度. 我采取的措施是, 调度器线程先结束, 但是需要满足: 输入线程结束, 所有的电梯线程完成任务陷入wait状态中, 待调度队列为空. 三个条件, 就可以避免二次调度得问题.

四 互测相关

测试别人bug主要是通过读读代码, 看看别人的架构, 然后黑盒测试, 看代码不花大量时间也看不出什么bug. 黑盒测试主要是测一些自己在自测过程中发现的一些坑点. 以及压力测试.

五 心得体会

这三次作业帮助我对java并发编程有了较为深刻得了解. 对于一些常见的java并发错误, 诸如死锁, 线程不安全等, 有了更为深刻的认识, 同时面向工程的考核, 使得对于工程化编程也有了一定了解和感触.

posted @ 2019-04-24 17:18  燈心  阅读(199)  评论(0编辑  收藏  举报