我永远爱着OOP——第二单元作业总结
第二单元的电梯真是愉♂快呢,多线程编程作为java编程OOP中的重要组成部分,通过这一个单元的学习,我也是有了很多全新的认识
那么下面就先例行一下公事
三次作业分析
第五次作业
设计分析
实现的电梯是很简单的,没有复杂的逻辑,主要目的应该也是帮助同学们入手多线程编程,加上课上对设计模式有所点拨,所以整体的设计应该是不难的,编码量也不大,只要处理好锁的关系和wait
与notify
的时机,不要出现死锁,基本是不会有什么问题的
下面给出我的设计,也就是中规中矩的生产者-消费者
模式
这里我封装了自己的一个容器Requester
(感觉什么东西加上个"者"后缀就很厉害呢),其实就是一个请求队列,实现了一下迭代器接口(后来发现不好用)
然后就是电梯线程和电梯线程,调度器线程没有启用,这个设计主要是为以后的扩展做了一点准备,所以这里的Scheduler
是个静态的
然后差不多就是这样了,邻接资源就是那个队列,使用的时候锁一下就好了
一个需要注意的地方是怎么控制输入结束的全局持续通知
这里是借用了信号量的思想,也就是设置了一个相应的属性,然后每次循环的时候查看一下这个信号,别的就没什么了
下面是整个程序的一些度量(忽略这只手)
因为有各种各样的输出语句,所以Elevator
类的方法好像有点多了,不过我确定一定很短,很简单
你看
方法的复杂度都很低,看起来还是可以的
测试
对于多线程的程序我不是很清楚怎么做单元测试,单元测试也只能保证模块功能正常,但是我们都知道,真正的问题往往出现在线程的控制上的。
分享课上同学们分享的也都是各种黑盒测试(个人是比较鄙弃全部依靠黑盒测试的行为,盲目的随机胡测也许很有效,但是绝不优雅),所以也欢迎大家分享如何富有逻辑地进行测试。
第六次作业
第六次加上了捎带规则,加了点楼层,加了负层,0层神秘消失(误),整体来说笔者的设计是这样的。
三次设计的思路都是坚持做到单一职责,毕竟分割职责可以在做修改和扩展的时候放心大胆(我一向很大胆,其实是因为分隔职责后便于测试,良好的测试是修改的保险丝)。
所以就是
- 输入线程只管输入,是一个无情的指令接收机器
- 电梯线程只管执行,是一个无情的指令执行机器
- 调度线程负责调度,完成(
无情地)指令分发的任务
然后这样的设计就出现了
!
相比上次的设计,没太多变化,就是调度器动起来了,然后肩负起分配指令的任务
然后电梯中需要有一个队列维护自己持有的指令,线程变成了单步执行,每层看一看有没有上下开关门事件发生,逻辑不算复杂
调度器就是不断尝试将指令放入电梯中(后来笔者发现这里设计的不好,调度器空转太多了,加一个wait
和notify
机制会更好,或者是join
,不过想来想去,抢占式或许是最优解,这个后面再说)
然后下面是整个程序的度量
可以看到调度器的复杂度比较高,其实主要是笔者作死的一个设计
笔者为指令和电梯运行加了一个方向的概念,因此自己给自己多设置了一层逻辑,没什么意义,也就是有MainRequest
和Direction
两个量都表征运行方向,每次更新都要更新俩,判断的时候用哪个也说不清,确实是很糟糕的设计,笔者在后面也把这个量给废弃了
测试
关于这次的测试,笔者给出一些个人思考的可能出现问题的情况
- 对于LOOK玩家,在电梯折返,或者说换向的时候打断,可能是一个边缘逻辑
- 对于开关门和来指令的并发情况
- 对于错过的指令的测试
第七次作业
设计分析
据说这是OOP作业难度的顶峰
鬼畜多电梯、负载、速度、随机突发随机指令,这几个情境下设计一个好的算法确实不容易
所以与其苦心分析设计,不如"实践检验真理",也就是通过本地测试时间来决定算法的性能(真是个小机灵鬼)
所以在说我的设计之前,我想理性(瞎胡说)地分析一下这次调度算法的考虑
1. 关于接人和方向
(笔者很讨厌上楼的人在电梯下行的时候就上电梯),在本次作业中,出于载荷的考虑,也是加上同向捎带的判断,电梯的性能应该会好一些,但是实际检验发现,这个判断的效果并不明显,并且有的时候会出现负优化,个人分析原因是在于40条指令,三个电梯的总容量有20多,所以即使面对40指令并发,对于电梯们来说,工作量也不是很大。
正因如此,像我们考虑的情景——“很多反向请求的人上电梯,导致同向的请求上不去”,其实发生率也不高,所以在这种情景下,大吞吐反而是很优的。
2. 细节优化
对于这个评测方式,其实很多细节优化的效果也是不错的(只指分数,不保证绝对时间差),比如电梯的选择顺序,是快的优先还是随便来;是近的优先还是随便来。这种细节上的选择也会有不错的收益。
3. 抢占式很重要
如果让调度器主动分指令,电梯就要记着一会要接谁,即使这个人不在电梯里,出于安全考虑,电梯也必须为这个人留个位置,那么在这种情景下,电梯一般是一直装不满的。
本人的算法基本是硬调度,没有动态调度,调度规则都是硬编码的。(不像那些大佬们在电梯运行的时候动态通信)
程序的逻辑就是每次楼层改变和进出人后就去中间的Commander
那里pull
一下,看看有没有自己能拿走的,这样可以保证最大利用电梯容量
然后pull
的逻辑是在Commander
中实现的,Commander
获取电梯的当前状态,然后判断队列中有没有他能拿走的
下面是程序的度量
看起来Commander
好像不太高兴了,因为笔者不小心把指令分割的逻辑放到了Commander
里面,这个是违反单一职责原则的,我在下面简单做一下拆分
可以看到,加上Spliter
后,Commander
的复杂度明显下降,此时Commander
中只剩下和分配有关的逻辑
方法的复杂度还是很不错的
测试
这次测试笔者主要关注了下面这几个点:
- 载荷,要保证不能超载,对于电梯载荷上限的数据要进行测试
- 需要拆分的指令,对于需要拆分的指令,特别是可以多次拆分的指令,要重点测试
- 效率,最好在测试的时候单步输出一下每时刻每座电梯中的人数,确保电梯有较高的载重
设计原则分析
- SRP(单一责任原则):这次设计基本是符合单一原则的,唯一不足的地方是,电梯是主动去获取指令的,获取指令的逻辑也在电梯类里面,可以考虑专门做一个获取器,然后电梯每次调用这个类的对象
- OCP(开放封闭原则):这三次作业其实需求扩展体现的并不明显,重点是需求变化,完全地不变更原设计是不现实的
- LSP(里氏替换原则):这三次作业的类基本继承层数都是1,没有父子类的问题
- ISP(接口分离原则): 三次作业不涉及接口问题
- DIP(依赖倒置原则):三次作业中的类基本没有继承层次,所以也不涉及依赖倒置的问题
UML时序图分析
好了,我完事了
上面就是本系列作业的设计了,下面说一些我想说的
相比于上个系列作业,这个系列突出的需求增加更加明显,也更加友好,不存在说两次需求相互矛盾的情况,对于设计优秀的同学,每次作业的编码量肯定是不大的
然后吧,这个时间效率这个事情,我觉得确实是一个摸索的过程,群里有人在说用NN,问题不大,其实我上面说到的自己去尝试算法,然后调整算法,其实就是个手动NN的过程(误),真正要是在实际中解决这种鬼畜电梯的问题,就是要靠NN来实现根据过去推将来的,用NN来实现对未来的分析预测,从而调整自身调度策略,获得最优调度
下面想重点说一下这个监听者模式,老师上课提了很多次,下课也听同学们有讨论,所以说一点个人的见解(以下都是胡说,理性参考)
监听者模式的提出,是为了能够让一个个的listener
动态地加入监听和退出监听,同时,在Commander
新消息发布的时候,可以通知所有监听者,java
的Swing
库就大量使用了这种设计
针对本次作业,笔者最开始是考虑过用这个模式的,每当输入一个指令就通知一下电梯,然后电梯取指令,刚好很多电梯,然后可以都注册为listener
但是存在以下几个问题:
- 所谓通知,在这里的体现就是把指令放进去,但是,在指令来的时候,电梯可能正处于上锁状态(例如,进出处理中),这时我们的
Commander
试图尝试往每个电梯里放指令,就会遇到锁 - 其实上面那个还不是最重要的,毕竟只是要等一下下就好,问题是当某个请求无法被任何电梯处理的时候,这个请求就要被存起来,这样的话,其实就又退回了我们的生产消费模型了(毕竟在
Swing
中,如果一个消息没有监听器,这个消息是会被扔掉的,但是显然我们这里不能这样)
所以,这个模式虽然很美,但是放在本系列作业中也许有些不当之处,当然,这只是我浅薄的认识,如果大家有更好的实现方式,也欢迎在下面留言,一起讨论,一起学习
我永远爱着OOP——第二单元作业总结的更多相关文章
- OO第二单元作业总结【自我反思与审视】
第二单元作业的完成史,就是一部心酸的血泪史…… 多线程的出现为我(们)打开一片广阔的天地,我也在这方天地摸爬滚打,不断成长!如果说第一单元之前还对Java语法有所了解的话,那么这单元学习多线程则完全是 ...
- BUAA OO 2019 第二单元作业总结
目录 总 架构 controller model view 优化算法 Look 算法 多种算法取优 预测未来 多线程 第五次作业 第六次作业 第七次作业 代码静态分析 UML 类图 类复杂度 类总代码 ...
- 【BUAA-OO】第二单元作业总结
第二单元作业总结 ——电梯恐惧症患者的极限自救 一. 第一次作业程序分析 1. 设计策略简略分析 线程:主线程.输入线程和电梯线程,另有一个持有请求队列的调度器,一个对输入进行处理的Req ...
- BUAA_OO第二单元作业总结——多线程
OO第二单元作业总结——多线程 单元任务 本单元主要的内容是通过模拟电梯的运行来熟悉多线程的实现,从简单的单部FAFS电梯开始,ALS电梯,到最后的多部ALS电梯. 一.设计策略分析总结 1.1 多线 ...
- 【OO学习】OO第二单元作业总结
OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...
- 电梯也能无为而治——oo第二单元作业总结
oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...
- OO第二单元作业小结
前言 转眼已是第九周,第二单元的电梯系列作业已经结束,终于体验了一番多线程电梯之旅. 第一次作业是单电梯的傻瓜调度,虽然是第一次写多线程,但在课程PPT的指引下,写起来还是非常容易:第二次作业是单电梯 ...
- 电梯模拟系统——BUAA OO第二单元作业总结
需求分析 官方需求 本次作业需要模拟一个多线程实时多电梯系统,从标准输入中输入请求信息,程序进行接收和处理,模拟电梯运行,将必要的运行信息通过输出接口进行输出. 本次作业电梯系统具有的功能为:上下行, ...
- oo第二单元作业总结
oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...
随机推荐
- 问题:这个新申请的内存为什么不能free掉?(已解决)
一.问题描述 先上代码, /*** 省略 ***/ uChar *base64 = NULL; /*** 省略 ***/ base64 = (一段内存) /*** 省略 ***/ base64 = s ...
- .net Core 2.0应用程序发布到IIS上注意事项
.net Core2.0应用程序发布window服务器报错容易错过的配置. 1.应用程序发布. 2.IIS上新建网站. 3.应用程序池选择无托管代码. 4.服务器上安装DotNetCore.1.0.1 ...
- JavaMail技术实现邮件发送转【】
1.导入2个jar包,mail.jar,activation.jar 2.导入的jar包与myeclipse中自带的javaee 中的javaee.jar中的javax.activation包及jav ...
- HBase之Table.put客户端流程
首先,让我们从HTable.put方法开始.由于这一节有很多方法只是简单的参数传递,我就简单略过,但是,关键的方法我还是会截图讲解,所以希望大家尽可能对照源码进行流程分析.另外,在这一节,我单单介绍p ...
- Java代码复用的三种常用方式:继承、组合和代理
复用代码是Java众多引人注目的功能之一.这句话很通顺,没什么问题,但问题在于很多人并不清楚“复用”是什么.就好像我说“沉默王二是一个不止会写代码的程序员”,唉,沉默王二是谁? 我们需要来给“复用”下 ...
- Javascript的原型继承,说清楚
一直以来对Javascript的原型.原型链.继承等东西都只是会用和了解,但没有深入去理解这门语言关于继承这方面的本质和特点.闲暇之余做的理解和总结,欢迎各位朋友一起讨论. 本文本主要从两段代码的区别 ...
- 多机部署redis5.0集群环境
redis5.0集群部署 一.集群介绍 Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施(installation). Redis 集群不支持那些需要同时处理多个键的 Redi ...
- Android--Tween补间动画
前言 Android除了支持逐帧动画之外,也提供了对补间动画的支持,补间动画就是指开发人员只需要指定动画的开始.动画结束的"关键帧",而动画变化的"中间帧"由系 ...
- 『Asp.Net 组件』Asp.Net 服务器组件 内嵌图片:自己的图片控件
代码: using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace ...
- Unity实现c#热更新方案探究(二)
转载请标明出处:http://www.cnblogs.com/zblade/ 一.IOS对DLL热更新的禁止 紧接上文,继续对C#热更新的研究.上文中,已经说了如何基于appDomain来实现对DLL ...