面向对象第二单元总结

本单元的作业是一系列的多线程电梯调度问题。三次作业分别要求完成单部多线程FAFS电梯、单部多线程可捎带电梯和多部多线程智能电梯。感觉主要难点是如何进行线程之间的合作以及同步控制。

一、设计策略

在第一次作业中,我主要通过使用ArrayBlockingQueue型的阻塞队列作为等待队列来保证线程的安全性,电梯线程使用轮训+sleep()的方法。在之后的两次作业中,我主要使用synchronized 关键字保证共享数据的安全,使用wait()、notify()方法来进行线程之间的合作。

二、基于度量分析程序结构

第一次作业

类图:

时序图:

分析:FAFS调度。在我的设计中,有两个线程,主线程和电梯线程。

  • 两个线程共享一个Mylist类的一个对象list。Mylist类成员变量包括一个ArrayBlockingQueue类的阻塞队列(作等待队列)和一个布尔类型的值flag来标志现在输入有无结束。
  • 主线程负责将读入的数据添加(put)到等待队列
  • 电梯线程从等待队列中拿到请求(getFirst)并将其送达目的地,送达后从等待队列中删除(remove)。
  • 在输入结束时,主线程使用setFlag将Mylist中的flag置位。
  • elevator轮巡时,如果现在的等待队列中无人(size==0)时,判断flag是否置位,置位则跳出死循环,电梯结束运行。

缺点:暴力轮训不是个好主意,会浪费CPU资源,wait()\notify()更合适一点。

度量:第一次作业比较简单,代码量、思维量都比较低,所以度量结果并没有什么问题。

第二次作业

类图:

时序图:

分析:这次作业要完成一个单部多线程可捎带的电梯。对于捎带,我是直接按照指导书上的要求写的。基本架构沿用了上次作业。

  • 依旧是主线程、电梯线程两个线程。两个线程共享一个Mylist类的对象list,其成员变量是一个ArrayList的数组。Mylist类实现add\remove\getMainRequest(电梯线程用来获得主请求)\getInPerson(电梯线程用来获得捎带请求)方法。
  • 主线程依旧负责将请求添加到队列,当输入接受,主线程添加一个null元素到请求队列。
  • 电梯每次选择一个主请求,并将其送达目的地。当电梯里没人,而且拿到的主请求是null时,跳出死循环。在这个过程中,每到达一层时,使用getInPerson方法判断是否有可捎带的人要上电梯;并判断电梯中是否有人要下。
  • 由于这次引入了负层,从-1到1,数字上差2,但是却只差一层,所以我选择使用一个数组硬编码来保存电梯的所有可运行楼层。

度量结果:主要问题出在elevator的stay(int floor)方法中,在这个方法,要计算要出去的人,可捎带的人,并进行开关门操作。从设计原则来看,这个方法违背了单一责任原则,集成了太多的功能,所以复杂度过高了,可以对功能进行合理的拆分。

第三次作业

类图:

时序图:为了减少工作量,我直接选择了让电梯自己在调度中心抢人,所以基本框架依旧是沿用的第二次,只是具体方法有所增改,在此不再赘述。

分析:主要谈一下第三次作业中与前两次的不同之处。由于第三次作业要调度三个电梯,电梯运行速度、容量、可到达楼层都有所不同且有些请求不能只通过一个电梯便送达,所以很多方法都要有所修改。

  • 对于要不能通过一部电梯到达的请求,我选择拆分成两个请求,并简单粗暴的把这些人先运到一楼进行电梯转乘。首先,重新实现一个Request类,其中设置needcontinue和canrun属性。当主线程添加请求到调度中心时,应该判断请求是否需要拆分,如果需要,将其拆成fromFloor->1,1->toFloor两端,第一段的needcontinue属性置true,第二段的canrun 先置false。当电梯中有人出去的时候,判断这个请求是否needcontinue,如果需要,将这个请求的第二段的canrun置true。
  • 请求队列中canrun属性为false的请求不能被运送
  • 调度器有一个数组,用于存放所有的楼层。每个电梯有一个reachable数组,数组元素为0或1,为0表示无法到达,为1表示能到达。在选择主请求、选择捎带请求时,均需判断请求能否通过这个电梯到达。
  • 在判断捎带请求时,考虑电梯容量,要保证可捎带的人+电梯中的人(+1(主请求不在电梯时))不超过电梯容量。

度量结果:由于在我的实现中,电梯是自己去调度中心(请求队列)抢人的,电梯线程需要实现很多方法来决定怎么调度,就非常的臃肿。主要问题是获得主请求(getMainRequest)和获得捎带请求(getInList)的方法过于复杂。

三、关于这单元程序中的bug

  1. 自己程序的bug。

    这一单元又是没有优化的一个单元,不过也是没被找到bug的一单元。我的调度策略基本是按照指导书来的,没有任何优化,大概也因为如此,程序的正确性在测试中表现还比较好,三次强测和互测没被找到bug(互测屋比较佛大概也是原因之一)。

    我自己debug主要是在第三次作业的中测。因为写完之后就急着提交了,并没有认真读一遍自己代码,而当时程序还有非常多bug。导致我输出调试了半天多才de完智障bug(大部分都是手误),可见脑子是个好东西,之后写完代码首先一定要先自己分析一遍,输出调试很可靠,但是太费时间了,问题最好在写代码和构思时就消灭掉。

  2. 如何发现别人bug

    互测基本就佛了,没有搭自己的评测机。交了一些随机数据,利用系统评测机判断正误,果不其然最后都是空刀。

四、心得体会

​ 线程安全方面,在一切使用共享数据的地方都要小心,分析不同的线程在此处可能的冲突,对共享数据加锁。在我的作业中,只使用synchronized 关键字进行加锁,之后可以学习使用一下其他锁机制。在这一单元中,我们学了一些经典的设计模式比如生产者消费者模式、工厂模式等,套用固定的设计模式的确有助于更快更好地分析解决问题。

posted @ 2019-04-23 17:32  zhyiuxan  阅读(92)  评论(0编辑  收藏  举报