BUAAOO Unit2博客作业
OO第二单元总结
本单元的三次作业围绕多线程展开,实现了一个电梯模拟的程序,经过三次的迭代,踩过好多坑,摸爬滚打终于完成了作业,撑到本单元结束。
在三次作业中,我没有构建调度器类实现全局的调度,只是采用自由竞争的方法,让所有电梯访问公共请求队列,竞争可用请求。所以共享对象只有这一个公共请求队列。
第一次作业
第一次作业只是简单的请求线程和电梯线程的实现和协同,虽然内容看听起来很简单,但是真的好难啊,这是第一次接触多线程,第一个多线程的程序,看了无数遍的“买月饼”才勉强写出来,结果也不太好。
-
同步块和锁的设置
- 这次作业中我构建了请求输入线程和电梯线程,构建了一个存放请求的等待队列的类,共享对象只有这一个队列类,为等待队列配备了一个安全存放和拉取请求方法的Access类,所以Access中的方法都设置了同步块。
- 电梯线程中根据请求队列中的情况来改变自身运行状态,所以状态转换也设置了同步块。
-
锁与同步块中处理语句直接的关系
- Access类中对请求的拉取和存放一定会改写队列,所以此处同步块中的内容一定要加锁,且这部分没有其他逻辑,只是对请求队列进行操作,所以同步块中处理语句都很有必要加锁,而且同步块较小。
- 电梯线程中有一个硕大的同步块,即状态转换逻辑块,每一次的状态转换都需要访问请求队列,电梯根据请求队列来决定上行or下行or等待or结束,所以这部分固然是需要锁的。但是它将状态转换与请求拉去杂糅了起来,导致同步块很大,也混杂了一些其他不相干的逻辑。所以这个同步块设置得较为失败。
-
调度器设计
本次作业中并没有构建调度器,只是简单地电梯线程主动去访问请求队列来拉取请求放到自己的内部处理队列中。
-
bug分析
此次作业中强测出现了WA的Bug,问题在于运行策略的设计较差,导致Night模式下比ALS策略差很多,但是课下测试的时候并未发现这个问题,导致强测3个点失分。并未出现线程安全的问题。
第二次作业
第二次作业还是很简单的,只需要根据指令动态增加电梯线程即可,所以结构相较第一次作业几乎没有变化,只是增加了输出类并进行输出互斥保护,以及输入请求的判断和新建电梯线程。
-
同步块和锁的设置
本次作业仍保留第一次作业中的两个同步块,同时增加了一个输出保护的同步块。在构建了一个输出的Output类,作为全局共享对象来保证输出互斥,因而在Output类的输出方法中设置了同步块。
-
锁与同步块中处理语句直接的关系
Access类的同步块和电梯线程的同步块同第一次作业,由于当时还未意识到电梯线程的同步块是存在问题的,所以并未修改。
输出方法的同步块中只有输出语句的一行代码,所以该锁的设置很有必要。
-
调度器设计
本次作业中仍没有构建调度器,所有电梯线程都主动去访问请求队列来竞争拉取请求,并存放到自己的内部处理队列中。
-
bug分析
这次作业中并未产生Bug,作业完成得比较顺利
第三次作业
第三次作业引入了不同类型的电梯,并且要求实现换乘,而且发现了之前电梯线程中状态转换的代码块设置的问题,所以此次对电梯类进行了略大的改动,并构建了层次化的电梯线程类。
-
同步块和锁的设置
本次作业保留了输出类中的同步代码块,将Access方法类中拉取请求的同步方法转移到电梯类中,存放请求的方法放在共享对象类中(反正都是单例模式,我觉得不会出现问题),并在共享对象中增添了控制所有电梯结束的参数以及访问方法。输出类中的同步块设置与第二次作业相同。
- 电梯线程中的状态转换代码块为一个同步块。这个代码块内部为根据共,并在该线程wait的时候告知共享队列。
- 电梯线程中还有拉取请求的方法。
-
锁与同步块中处理语句直接的关系
- 电梯线程中状态转换要根据享队列的状态来改变电梯自身的状态,并将自己的状态告知共享对象,所以要增加共享对象的锁,这是很必要的,并且将其他逻辑独立出这个同步块,所以同步块处理语句基本都与共享对象访问相关。
- 存放请求和拉取请求方法的同步块中也均与共享访问对象有关,所以这个部分也有加锁。
-
调度器设计
这次仍没有设置调度器,仍是采用自由竞争的方案,并在下电梯的时候生成新的请求放到共享对列中,由共享对列决定该乘客请求是否保留还是移除。把该拉取请求的方法放到电梯类中的原因是要根据不同电梯的特性决定是否拉取该请求(这就是没有总调度器导致的一点不好之处)。
-
bug分析
这次作业,没有出现Bug,但是值得一提的是在课下做作业的时候,发现了结束电梯时会轮询共享对象中的停止标志,导致cpu时间特别高,最终删除了几个
notifyAll才解决问题,之前一直惧怕死锁问题,所以能加notifyAll的位置基本都加了,导致wait-notify机制形同虚设,导致了轮询问题,所以notifyAll不能随便加呀,吸取教训了!
架构及其可扩展性
最终的作业的类图和协作图如下:
-
类图:
-
协作图:
-
时序图
-
功能与性能的权衡
在这三次作业中,我基本都以功能为主,并未过多针对性能做太多优化,在保证了功能正确的基础上对特殊情况稍做处理,如早晚模式的特殊算法、请求队列的划分以及特殊处理,但由于对Night模式选择的调度算法性能太差使得三个点爆掉,在稍作修改后在第二、三次作业中都取得了还不错的性能分。
-
可扩展性
对于电梯种类来说可扩展性较好,能够实现各种速度、各种可停靠楼层的电梯。但是调度算法的扩展性不算太好,因为状态转换为一个整块的函数,所以更改必须对整块函数进行整体修改。由于没有调度策略类,调度策略杂糅在调度算法中,所以针对调度策略扩展性较差。所以第三次作业的可扩展性相对一般,由于对这个问题的理解也不是太清楚,除了算法想不到什么扩展的方向,所以也并未做太多设置。
互测策略
这一单元是多线程,所以遇上一单元有很大的不同就是程序执行结果具有不确定性,不像第一单元本地评测机hack到了,交上去就一定能hack出问题,所以这一单元没有去搭建自己的自动评测机,也不太会搭,所以只是手动构造了一些针对性数据以及之前作业的中强测数据对房间里的代码进行简单的测试,进行了输出结果的正确性自动判断以及死锁问题的甄别,结果hack效果不尽人意。不过我认为这一单元更适合阅读房间里的代码,不知道是不是大家代码风格有所进步,这次的代码读起来比上一单元更清晰了,而且阅读过程中的也见识了不同的思路和策略,虽然hack的效果不尽人意,但是还是很有收获的。
心得体会
第二单元相较第一单元收获更多,从0逐渐掌握了多线程程序设计和debug,真是个不错(痛哭)的体验!回想第一次作业的艰难过程以及第二次作业的畅快还有第三次作业的小小成就感,人间值得!
这一单元主要问题就在线程安全问题上,在初上手的时候就十分惧怕这个东西,所以把所有涉及共享对象的的地方全部加上了锁,把所有离开同步块的地方全部加上了notifyAll,心想这下肯定不会产生线程安全问题,心中暗喜。。。然而轮询问题来了,所以针对线程安全问题还是要全面思考,找到可能会产生冲突的地方加上锁,需要唤醒其他线程的地方再唤醒,唉,想想当时“得意”的东西让我de了半天的bug真是血泪史啊ww。
在这个单元中层次化设计主要体现在第三次作业电梯设计上,应用了工厂模式来解决问题。我的程序在策略算法上层次化得不算太好,这也是这一单元比较大的遗憾吧。
总的来说,这一单元的作业真的让我学到了很多,至少对于线程安全已经有所体会,对于层次化设计也比第一单元得心应手,感谢过程中帮助我的老师助教和兄弟们!!!

浙公网安备 33010602011771号