2019OO第二单元作业总结
OO第二单元的作业主题是模拟电梯。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第一次作业:FAFS傻瓜调度-单电梯模拟
1、多线程的协同和同步控制
本次作业的线程只有两个:调度器线程和电梯线程。调度器线程负责接收指令并维护一个请求队列,请求队列使用单例模式构造,由于电梯采用傻瓜调度,因此电梯线程每次从调度器的请求队列中取一个指令执行。在本次作业中,调度器线程和电梯线程唯一需要共享的对象是请求队列,因此为了保证线程安全,应在请求队列的getqueue方法前加上synchronize关键字。
2、程序结构
本次作业有4个类:Main类、Elevator类、Dispatcher类、InputHandler类。类图如下:
类复杂度分析如下:
方法复杂度分析如下:
3、自己程序的bug
由于本次作业比较简单,我们首次接触多线程可能会出现有线程提前结束或无法结束的情况,这可能是在写代码的时候遇到的最大的困难,逻辑上的问题其实并没有多少,可以把这次的作业抽象为一个简单的生产者-消费者问题。公测和互测均未发现bug。
4、如何发现别人的bug
本次作业比较简单,因此发现别人的bug也不容易,我没有发现别人有逻辑上的bug。至于线程方面的bug,我有听说有些程序可能输入数据后过一段时间再输入ctrl+D会出现无法结束的情况,但我本人并没有发现这样的bug。测试策略方面,与第一单元的策略略有不同的是,我可以选择在不同的时间投放测试数据,或是在同一时间投放几个数据,以测试线程的运行是否正确。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第二次作业:ALS捎带调度-单电梯模拟
1、多线程的协同和同步控制
本次作业仍然是单电梯的调度,因此线程和第一次作业一样有两个:调度器线程和电梯线程。调度器线程的职责没变:负责接收指令并维护一个单例模式的请求队列。本次电梯的调度策略采用ALS捎带算法,电梯需要有一个主请求和若干个捎带请求,因此电梯需要自己维护一个请求队列。在本次作业中,调度器线程和电梯线程需要共享的对象还是调度器的请求队列,因此在调度器的getqueue方法前加上synchronize关键字。
2、程序结构
本次作业的类相比上一次并没有什么改变,还是4个类:Main类、Elevator类、Dispatcher类、InputHandler类。类图如下:
类复杂度分析如下:
方法复杂度分析如下:
3、程序bug
本次作业我完全采用了指导书中描述的ALS算法,因此正确性方面没有出问题,但同时性能分也基本没有。
4、如何发现别人的bug
与我同组的同学也基本采用同样的ALS算法,因此也很难找出逻辑上的漏洞。我没有发现bug。测试策略与本单元第一次作业基本相同,会加入一些可以捎带的请求组合来测试捎带算法的正确性与线程安全。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第三次作业:SS智能调度-多电梯模拟
1、多线程的协同和同步控制
本次作业是多电梯的调度,一共有三部电梯,因此是一个调度器线程和三个电梯线程并行。每部电梯能到达的楼层和运行速度均不同,乘客的请求可能无法由一部电梯直接送达,需要换乘,换乘的请求需要注意必须等到第一次电梯把乘客运送到换乘楼层之后第二部电梯才能接上乘客继续运送,注意到这点的情况下,我采用了电梯在运送完换乘的乘客后再将乘客放回请求队列并修改其属性信息,以保证正确的时间的先后顺序逻辑。本次电梯的调度策略并没有规定,我仍然采用ALS捎带算法。在本次作业中,调度器的请求队列可能被三部电梯同时访问而产生线程安全问题,因此我在每一个电梯上/下人的过程中都锁住了dispatcher这个对象以防止出问题。
2、程序结构
这次作业比上一次作业在捎带算法方面没有什么区别,只是多了需要换乘的乘客,因此我多了一个TransferPersonRequest类来处理需要换乘的请求,总共有5个类:Main类、Elevator类、Dispatcher类、InputHandler类、TransferPersonRequest类。类图如下:
类复杂度分析如下:
方法复杂度分析如下:
3、程序bug
这次的bug很多,强测正确率8/20,错误主要是因为在处理有关3层的调度的时候出现了很严重的逻辑错误,导致有关3楼的需要换乘的请求基本都无法正确执行,互测也被人抓着这个bug无限hack。这也是因为我的换乘请求处理不够全面,从上面的复杂度分析也可以看出,我的代码中有些方法复杂度太高,难以看出错误。
4、如何发现别人的bug
本次作业,有关3楼的换乘请求的处理确实是一个难点,因此我首先就用关于3楼的请求来测试其他同学,如1-FROM-3-TO-4,2-FROM-2-TO-3等,结果和我的情况差不多,互测的同学也基本在处理3层的请求时出现了问题,可能会出现乘客在3楼无限循环出入电梯的情况。其他的问题由于有了前两次作业的基础,并没有发现很多。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
心得体会
这三次作业都是有关多线程的。多线程程序中最重要的一点就是保证线程安全,我这三次作业基本没有出现线程安全的问题,这是很好的一点。关于线程安全需要注意的地方总结一下就是:某个共享资源(对象)在同时被多个线程访问并进行读写操作时可能会出现线程安全的问题,为了避免线程安全问题,可以采用在访问共享对象的时加锁、尽量不要将自己对象的修改权随便交给其他对象、在外部对象想要访问自己对象中的元素时,返回该元素的一个副本而不是该元素本身等方法。在设计原则方面,最好是每个对象做好自己的事,尽量减少与其他对象的交互,即所谓的“解耦”,这样既更加符合面向对象的思想,也可能可以减少锁的使用,提高多线程并行的效率。此外需要特别提出的一点就是,在这三次作业中,中测出现次数很多的问题是CPU_TIME_LIMIT_EXCEED,即CPU时间过长,这往往是由于程序中存在轮询的情况而导致的,比如在调度器请求队列中没有请求时,电梯可能会无限循环去检测请求队列中有没有请求,这样电梯线程就会一直占用着CPU资源,导致CPU时间超时。解决的办法也很简单,只需要利用sleep()或是wait()和notifyAll(),在电梯检测到调度器请求队列为空的时候让电梯线程等待一段时间或是等待直到被唤醒即可。