OO第二单元作业总结

一.多线程单部电梯——傻瓜调度


 

1.设计策略

  •   这次作业整体来说不是很难,是多线程的入门,主要目的就是让我们认识,了解一下什么是多线程。所以主要时间花费不在架构的设计,在于认识,理解,如何去使用多线程。思考后的想法是:一个用于存储人(Person)的类,一个用于解析输入的线程,一个电梯线程,并且维护一个共享队列,其中包括电梯队列和等待队列。有人来就进入等待队列,上了电梯就进入电梯队列,当输入线程结束且共享队列(两个队列)均为空,电梯线程结束。想法非常简单也很现实,但一开始对多线程不是很懂,实现起来用了一些时间

2.类图

  •   各个类的分工还是比较明确的,后面几次作业改动也不是很多,只是针对新的问题加入了一些新的方法。方法的规模、个数也不是很多,类的总代码规模比较小

3.度量分析

  •   方法和类的复杂度都不是很高,这回设计的比之前的作业好,这回作业不是特别复杂

4.自身bug分析

  •   本次作业傻瓜调度,一次只运一个人,不要求性能分,我也只求电梯正常运行,只要架构还可以,线程结束条件控制好,一般没什么bug(但是留有一个隐患,后面有说到)

5.互测bug分析

  •   大家基本也是傻瓜调度,没什么明显的bug。只是一些没有控制好结束条件的,电梯线程可能还没有送完人就结束了,或是停不下来,但是比较少

二.多线程单部电梯——可捎带(ALS)


 

1.设计策略

  •   基本和第一次想法类似,只是加入了一些新的方法,如 personCarryIn() 和 personCarryOut() 等,到达每一层的时候看有没有人要上(捎带)下电梯,主要是条件的判断,大体上就是指导书的思路

2.类图

  •   图太长了,大概就是这样。类都没有变,只是加了一些方法

3.度量分析

  •   复杂度的控制还可以,相比第一单元有了一点进步,各个模块耦合度不是很高。整体没什么太大的变化

4.自身bug分析

  •   目前来看没遇到什么bug,因为只是简单的模仿日常我们使用的电梯,并没有写(也不会写)什么特别复杂的调度策略来追求性能分,还是以保证正确性为主(仍然有一个隐患)

5.互测bug分析

  •   互测中也没有发现明显的bug
  •   这一次出现了由于调度导致的线程安全问题,听说一些同学的电梯会飞到几百层或者降到负的很多层。线程安全还是不能忽视的,但这一点也正是多线程最难的一点,我还得继续运用体会,不实践几次错几次还是不知道应该怎么维护线程安全,因为这类问题一般都是由一些极细极小的点引发的,很难注意到

三.多线程多部电梯——限制时间内自由调度


1.设计策略

  •   还是继承了之前的想法,只是这一次需要三个电梯队列,一个等待队列。因为等待队列对所有电梯来说应该是公有的,而电梯队列是每个电梯私有的,也就是上了电梯的人。同时,将之前的电梯类改为一个抽象类,只负责实现电梯的基本功能,这三部电梯(线程)分别继承这个抽象类。而且,因为限制了电梯可以停靠的楼层和负载限制,在维护的共享队列中加入了各电梯的负载限制和可停靠楼层,加入一些判断条件。想法挺简单,实现起来比较复杂,也遇到了很多问题,但是自己也没有其他更好的想法了,还是以正确性优先。对于需要中转的乘客,没有想到什么好的策略,因为1楼是所有电梯都能到的,因此我希望把所有需要中转的人都送去1楼,然后再从1楼去目的地,花费时间会很多,但这种想法确实比较简单
  •   ①首先判断有没有电梯能直达,即一部电梯即可完成工作,否则这个乘客就是需要中转的
  •   ②对于需要中转的乘客,先送去一楼,在他上电梯时,先将他的目的地改为1楼,加入这个电梯的电梯队列,直接 move(1) 即可。在一楼下去后,将他从电梯队列剔除,再次加入等待队列,将他的起始楼层改为1,并且将他的目的地恢复为之前的(这种想法可能在实际操作中有误,但只能想出这样的方法了)
  •   (我对于①②的协调没处理好)

2.类图

  •   (学习到了如何将uml类图保存并上传,而不是自己截图)
  •   主要的方法都在 SharedQueue 内,方法写的有些过多了,这也是我的一大缺点,好像设计中没有一个明确的调度器之类的东西,一直在以这个共享队列为调度器,对调度器的理解还不是很到位
  •   整体上,类的结构没什么太大的变化

3.度量分析

  •   平均下来,复杂度控制的还可以(图只截取了重要的部分)

4.自身bug分析

  •   也不算是什么bug,算是没有完成的部分
  •   对于需要中转的人,如何去接他,这一问题我没有解决。如一个乘客可由A电梯直接接送,A电梯已经去接人了,我希望B、C电梯可以原地等待,但是没有做到。因为对于B、C电梯来说,他们认为这个乘客是需要中转的。每部电梯只能完成自己的基础功能,电梯之间的互相协作我没有处理好,因此这次作业仅是一次有效作业
  •   还是需要一个调度器,要不然实现起来太复杂了,多线程本来就很复杂,容易把自己写懵
  •   一直存在的隐患:用Arraylist作为队列使用。电梯队列并不是严格意义的队列,可能有中途下的人,因此需要一个能够删除任意位置元素的“数组”。而Arraylist本身就是线程不安全的,但这几次似乎没有因Arraylist而产生的bug,但这毕竟是一个隐患,在网上查阅可以考虑用Vector代替,Vector源代码中的方法都加了synchronize关键字。同时,在每层遍历等待队列寻找有人是否能上电梯时,也涉及到了一个问题,如下
synchronized void personCarryIn(int floor, int terminal,
                                    String elevatorId) {
        ArrayList<Person> queue = queueSelect(elevatorId);
        List<Integer> list = listSelect(elevatorId);
        for (int i = 0; i < waitQueue.size(); i++) {
            Person p = waitQueue.get(i);
            if (personNeedIn(floor, terminal) && p.getBegin() == floor
                && !elevatorIsFull(elevatorId) &&
                canReach(list, terminal)) { 
                p.in(elevatorId);
              p.changeState();
                queue.add(p);
                removeFromWq(p);
                i--;
            }
        }
    }

 

重要的是最后的 i-- ,没有这一步则会报错(可能有其他更好的方法并不会遇到这个问题)

5.可能的互测bug

  •   由于有三个电梯线程,这次的bug有很多不可复现,也很难调试出来,终究还是因为线程安全问题。在写多线程时必须时刻注意线程是否安全

四.测试方法


 

  •   如果有评测机,可以用评测机测试,我写不出评测机也没有认识的同学有,因此就自己造一些有针对性的数据进行测试
  •   打印出一些提示语句帮助debug,这应该是一个不错的方法,因为多线程很复杂,很难考自己理清思路去debug
  •   关于线程安全问题,主要是注意“共享变量”这种东西,还有就是使用Java白给已经封装好的类时,注意其本身的线程安全性,如Arraylist、Hashmap等
  •   和第一单元相比测试策略差异不是很大,能实现自动化最好,实现不了只能手造数据,但尽量使数据具有针对性,代表性,在保证基本功能后可以加大数据量测试自己程序的稳定性以及线程安全性

五.心得体会


 

  •   这一单元对于多线程的学习中,不仅加强了对多线程的理解,更是深刻认识到了线程安全的重要性。一些平常没有太注意的小问题,就可能在多线程问题中被放大,并且会导致很严重的后果。因此,在写每一行代码时,一定要思考这一步是否是线程安全的。当然,能提前注意到最好,一些实在想不到或者难以理解的点,只能等到出错后再回来调试。设计肯定不是一开始就是完美的,测试数据也是完善我们设计的一部分,一定不能害怕错误,发现错误改正错误才是我们应该做的
  •   在开始设计程序写代码时,一定要注意自己的架构,切忌偷懒。“面向对象”的设计,一定要划分好各个类的作用,这样写出来的代码结构容易扩展,自己思路也不会太混乱。就这几单元作业来说,电梯,调度器,请求队列等几部分如果有了明确的划分,在电梯功能增加时,也就不至于每次重构。这几次作业我也体会到了更多的面向对象的思想
posted @ 2019-04-21 16:54  Geraint23  阅读(186)  评论(0编辑  收藏  举报